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:
|
||||
- macos-latest
|
||||
ocaml-compiler:
|
||||
- 5.1.x
|
||||
- 5.2.x
|
||||
local-packages:
|
||||
- eio eio_posix eio_main
|
||||
|
||||
@ -44,7 +44,7 @@ jobs:
|
||||
with:
|
||||
opam-pin: false
|
||||
opam-depext: false
|
||||
ocaml-compiler: ocaml.5.1.0,ocaml-option-mingw
|
||||
ocaml-compiler: ocaml.5.2.0,ocaml-option-mingw
|
||||
opam-repositories: |
|
||||
dra27: https://github.com/dra27/opam-repository.git#windows-5.0
|
||||
normal: https://github.com/ocaml/opam-repository.git
|
||||
@ -57,6 +57,7 @@ jobs:
|
||||
- run: opam exec -- dune build
|
||||
- run: opam exec -- dune runtest
|
||||
- run: opam exec -- dune exec -- ./examples/net/main.exe
|
||||
- run: opam exec -- dune exec -- ./examples/fs/main.exe
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
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
|
||||
|
||||
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:
|
||||
RUN sudo ln -sf /usr/bin/opam-2.1 /usr/bin/opam
|
||||
# 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:
|
||||
RUN opam install utop fmt
|
||||
# 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,
|
||||
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
|
||||
|
||||
|
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][]
|
||||
|
||||
# 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.
|
||||
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 -->
|
||||
|
||||
* [Motivation](#motivation)
|
||||
* [Current Status](#current-status)
|
||||
* [Structure of the Code](#structure-of-the-code)
|
||||
* [Getting OCaml 5.1](#getting-ocaml-51)
|
||||
* [Eio packages](#eio-packages)
|
||||
* [Getting OCaml](#getting-ocaml)
|
||||
* [Getting Eio](#getting-eio)
|
||||
* [Running Eio](#running-eio)
|
||||
* [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,
|
||||
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.
|
||||
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 packages
|
||||
|
||||
- [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_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_linux][] provides a Linux io_uring backend for these APIs.
|
||||
- [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_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 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:
|
||||
|
||||
```
|
||||
opam switch create 5.1.1
|
||||
opam switch create 5.2.0
|
||||
```
|
||||
|
||||
## Getting Eio
|
||||
@ -135,18 +129,18 @@ prompt and return after each line.)
|
||||
# open Eio.Std;;
|
||||
```
|
||||
|
||||
This function writes a greeting to `stdout` using [Eio.Flow][]:
|
||||
This function writes a greeting to `out` using [Eio.Flow][]:
|
||||
|
||||
```ocaml
|
||||
let main ~stdout =
|
||||
Eio.Flow.copy_string "Hello, world!\n" stdout
|
||||
let main out =
|
||||
Eio.Flow.copy_string "Hello, world!\n" out
|
||||
```
|
||||
|
||||
We use [Eio_main.run][] to run the event loop and call `main` from there:
|
||||
|
||||
```ocaml
|
||||
# Eio_main.run @@ fun env ->
|
||||
main ~stdout:(Eio.Stdenv.stdout env);;
|
||||
main (Eio.Stdenv.stdout env);;
|
||||
Hello, world!
|
||||
- : 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.
|
||||
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.
|
||||
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
|
||||
# Eio_main.run @@ fun _env ->
|
||||
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);;
|
||||
+Main would print "Hello, world!\n"
|
||||
- : unit = ()
|
||||
@ -185,8 +179,7 @@ The [Eio_mock][] library provides some convenient pre-built mocks:
|
||||
```ocaml
|
||||
# #require "eio.mock";;
|
||||
# Eio_main.run @@ fun _env ->
|
||||
let mock_stdout = Eio_mock.Flow.make "mock-stdout" in
|
||||
main ~stdout:mock_stdout;;
|
||||
main (Eio_mock.Flow.make "mock-stdout");;
|
||||
+mock-stdout: wrote "Hello, world!\n"
|
||||
- : 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.
|
||||
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
|
||||
(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.
|
||||
|
||||
Third-party tools, such as [Olly][], can also consume this data.
|
||||
[examples/trace](./examples/trace/) shows how to consume the events manually.
|
||||
|
||||
## Cancellation
|
||||
@ -317,22 +305,23 @@ For example:
|
||||
```ocaml
|
||||
# Eio_main.run @@ fun _env ->
|
||||
Switch.run (fun sw ->
|
||||
Fiber.fork ~sw
|
||||
(fun () -> for i = 1 to 3 do traceln "i = %d" i; Fiber.yield () done);
|
||||
traceln "First thread forked";
|
||||
Fiber.fork ~sw
|
||||
(fun () -> for j = 1 to 3 do traceln "j = %d" j; Fiber.yield () done);
|
||||
traceln "Second thread forked; top-level code is finished"
|
||||
);
|
||||
for i = 1 to 3 do
|
||||
Fiber.fork ~sw (fun () ->
|
||||
traceln "Job %d starting" i;
|
||||
Fiber.yield ();
|
||||
traceln "%d done" i;
|
||||
);
|
||||
done;
|
||||
traceln "All child fibers forked";
|
||||
);
|
||||
traceln "Switch is finished";;
|
||||
+i = 1
|
||||
+First thread forked
|
||||
+j = 1
|
||||
+Second thread forked; top-level code is finished
|
||||
+i = 2
|
||||
+j = 2
|
||||
+i = 3
|
||||
+j = 3
|
||||
+Job 1 starting
|
||||
+Job 2 starting
|
||||
+Job 3 starting
|
||||
+All child fibers forked
|
||||
+1 done
|
||||
+2 done
|
||||
+3 done
|
||||
+Switch is finished
|
||||
- : unit = ()
|
||||
```
|
||||
@ -397,6 +386,7 @@ let run_client ~net ~addr =
|
||||
Switch.run ~name:"client" @@ fun sw ->
|
||||
traceln "Client: connecting to server";
|
||||
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)
|
||||
```
|
||||
|
||||
@ -934,6 +924,9 @@ The mock backend provides a mock clock that advances automatically where there i
|
||||
- : 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
|
||||
|
||||
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 () =
|
||||
Eio_main.run @@ fun env ->
|
||||
Switch.run @@ fun sw ->
|
||||
let pool =
|
||||
Eio.Executor_pool.create
|
||||
~sw (Eio.Stdenv.domain_mgr env)
|
||||
~domain_count:4
|
||||
in
|
||||
main ~pool
|
||||
let dm = Eio.Stdenv.domain_mgr env in
|
||||
main ~pool:(Eio.Executor_pool.create ~sw ~domain_count:2 dm)
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```ocaml
|
||||
let main ~domain_mgr =
|
||||
Switch.run @@ fun sw ->
|
||||
let pool =
|
||||
Eio.Executor_pool.create ~sw domain_mgr ~domain_count:4
|
||||
in
|
||||
let main ~pool =
|
||||
let test n =
|
||||
traceln "sum 1..%d = %d" n
|
||||
(Eio.Executor_pool.submit_exn pool ~weight:1.0
|
||||
@ -1054,7 +1039,9 @@ let main ~domain_mgr =
|
||||
<!-- $MDX non-deterministic=output -->
|
||||
```ocaml
|
||||
# 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...
|
||||
+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.
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
- [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.
|
||||
|
||||
## 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
|
||||
[Domainslib]: https://github.com/ocaml-multicore/domainslib
|
||||
[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/
|
||||
[Eio.Process]: https://ocaml-multicore.github.io/eio/eio/Eio/Process/index.html
|
||||
[Dev meetings]: https://docs.google.com/document/d/1ZBfbjAkvEkv9ldumpZV5VXrEc_HpPeYjHPW_TiwJe4Q
|
||||
[Olly]: https://github.com/tarides/runtime_events_tools
|
||||
[eio-trace]: https://github.com/ocaml-multicore/eio-trace
|
||||
[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:
|
||||
RUN sudo ln -sf /usr/bin/opam-2.1 /usr/bin/opam
|
||||
# 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):
|
||||
RUN mkdir eio
|
||||
WORKDIR eio
|
||||
|
@ -45,6 +45,19 @@ let () =
|
||||
"metrics", `List metrics;
|
||||
]
|
||||
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 [
|
||||
"config", `Assoc [
|
||||
"uname", `String uname;
|
||||
"backend", `String env#backend_id;
|
||||
"recommended_domain_count", `Int (Domain.recommended_domain_count ());
|
||||
];
|
||||
"results", `List (List.map run benchmarks);
|
||||
]
|
||||
|
Binary file not shown.
@ -1,5 +1,5 @@
|
||||
<?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>
|
||||
<g>
|
||||
<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 "/>
|
||||
</symbol>
|
||||
<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 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 "/>
|
||||
</symbol>
|
||||
<symbol overflow="visible" id="glyph1-17">
|
||||
<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 overflow="visible" id="glyph1-3">
|
||||
<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 overflow="visible" id="glyph1-18">
|
||||
<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 "/>
|
||||
<symbol overflow="visible" id="glyph1-4">
|
||||
<path style="stroke:none;" d=""/>
|
||||
</symbol>
|
||||
<symbol overflow="visible" id="glyph1-19">
|
||||
<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 overflow="visible" id="glyph1-5">
|
||||
<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-20">
|
||||
<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 overflow="visible" id="glyph1-6">
|
||||
<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-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 "/>
|
||||
</symbol>
|
||||
<symbol overflow="visible" id="glyph1-22">
|
||||
<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 "/>
|
||||
<symbol overflow="visible" id="glyph1-12">
|
||||
<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 overflow="visible" id="glyph1-23">
|
||||
<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 "/>
|
||||
<symbol overflow="visible" id="glyph1-13">
|
||||
<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-24">
|
||||
<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 "/>
|
||||
<symbol overflow="visible" id="glyph1-14">
|
||||
<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-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 "/>
|
||||
</symbol>
|
||||
<symbol overflow="visible" id="glyph1-26">
|
||||
<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 "/>
|
||||
<symbol overflow="visible" id="glyph1-19">
|
||||
<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 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 "/>
|
||||
</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>
|
||||
<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 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 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 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 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 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 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 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 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 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 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 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 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>
|
||||
</defs>
|
||||
<g id="surface2">
|
||||
<rect x="0" y="0" width="1280" height="152" 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 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 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 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 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 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 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 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 "/>
|
||||
<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 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 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 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 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 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 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 1153.917969 0 L 1153.917969 184 "/>
|
||||
<g style="fill:rgb(40%,40%,40%);fill-opacity:1;">
|
||||
<use xlink:href="#glyph0-1" x="4" y="147.503906"/>
|
||||
<use xlink:href="#glyph0-2" x="11.582031" y="147.503906"/>
|
||||
<use xlink:href="#glyph0-3" x="18.935547" y="147.503906"/>
|
||||
<use xlink:href="#glyph0-4" x="25.533203" y="147.503906"/>
|
||||
<use xlink:href="#glyph0-5" x="33.138672" y="147.503906"/>
|
||||
<use xlink:href="#glyph0-6" x="36.953125" y="147.503906"/>
|
||||
<use xlink:href="#glyph0-7" x="44.570312" y="147.503906"/>
|
||||
<use xlink:href="#glyph0-8" x="49.503906" y="147.503906"/>
|
||||
<use xlink:href="#glyph0-9" x="52.837891" y="147.503906"/>
|
||||
<use xlink:href="#glyph0-5" x="60.455078" y="147.503906"/>
|
||||
<use xlink:href="#glyph0-9" x="64.269531" y="147.503906"/>
|
||||
<use xlink:href="#glyph0-8" x="71.886719" y="147.503906"/>
|
||||
<use xlink:href="#glyph0-10" x="75.220703" y="147.503906"/>
|
||||
<use xlink:href="#glyph0-8" x="82.322266" y="147.503906"/>
|
||||
<use xlink:href="#glyph0-11" x="85.65625" y="147.503906"/>
|
||||
<use xlink:href="#glyph0-8" x="91.908203" y="147.503906"/>
|
||||
<use xlink:href="#glyph0-12" x="95.242188" y="147.503906"/>
|
||||
<use xlink:href="#glyph0-13" x="102.583984" y="147.503906"/>
|
||||
<use xlink:href="#glyph0-14" x="110.189453" y="147.503906"/>
|
||||
<use xlink:href="#glyph0-5" x="114.232422" y="147.503906"/>
|
||||
<use xlink:href="#glyph0-15" x="118.046875" y="147.503906"/>
|
||||
<use xlink:href="#glyph0-16" x="125.681641" y="147.503906"/>
|
||||
<use xlink:href="#glyph0-5" x="133.316406" y="147.503906"/>
|
||||
<use xlink:href="#glyph0-17" x="137.130859" y="147.503906"/>
|
||||
<use xlink:href="#glyph0-11" x="144.736328" y="147.503906"/>
|
||||
<use xlink:href="#glyph0-1" x="4" y="179.503906"/>
|
||||
<use xlink:href="#glyph0-2" x="11.582031" y="179.503906"/>
|
||||
<use xlink:href="#glyph0-3" x="18.935547" y="179.503906"/>
|
||||
<use xlink:href="#glyph0-4" x="25.533203" y="179.503906"/>
|
||||
<use xlink:href="#glyph0-5" x="33.138672" y="179.503906"/>
|
||||
<use xlink:href="#glyph0-6" x="36.953125" y="179.503906"/>
|
||||
<use xlink:href="#glyph0-7" x="44.570312" y="179.503906"/>
|
||||
<use xlink:href="#glyph0-8" x="49.503906" y="179.503906"/>
|
||||
<use xlink:href="#glyph0-9" x="52.837891" y="179.503906"/>
|
||||
<use xlink:href="#glyph0-5" x="60.455078" y="179.503906"/>
|
||||
<use xlink:href="#glyph0-9" x="64.269531" y="179.503906"/>
|
||||
<use xlink:href="#glyph0-8" x="71.886719" y="179.503906"/>
|
||||
<use xlink:href="#glyph0-10" x="75.220703" y="179.503906"/>
|
||||
<use xlink:href="#glyph0-8" x="82.322266" y="179.503906"/>
|
||||
<use xlink:href="#glyph0-11" x="85.65625" y="179.503906"/>
|
||||
<use xlink:href="#glyph0-8" x="91.908203" y="179.503906"/>
|
||||
<use xlink:href="#glyph0-12" x="95.242188" y="179.503906"/>
|
||||
<use xlink:href="#glyph0-13" x="102.583984" y="179.503906"/>
|
||||
<use xlink:href="#glyph0-14" x="110.189453" y="179.503906"/>
|
||||
<use xlink:href="#glyph0-5" x="114.232422" y="179.503906"/>
|
||||
<use xlink:href="#glyph0-15" x="118.046875" y="179.503906"/>
|
||||
<use xlink:href="#glyph0-16" x="125.681641" y="179.503906"/>
|
||||
<use xlink:href="#glyph0-5" x="133.316406" y="179.503906"/>
|
||||
<use xlink:href="#glyph0-17" x="137.130859" y="179.503906"/>
|
||||
<use xlink:href="#glyph0-11" x="144.736328" y="179.503906"/>
|
||||
</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 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%,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%,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(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%,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.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 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 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(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 style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||
<use xlink:href="#glyph0-18" x="683.339844" y="58"/>
|
||||
<use xlink:href="#glyph0-7" x="688.044922" y="58"/>
|
||||
<use xlink:href="#glyph0-2" x="692.978516" y="58"/>
|
||||
<use xlink:href="#glyph0-3" x="700.332031" y="58"/>
|
||||
<use xlink:href="#glyph0-19" x="706.929688" y="58"/>
|
||||
<use xlink:href="#glyph0-20" x="714.3125" y="58"/>
|
||||
<use xlink:href="#glyph0-13" x="717.646484" y="58"/>
|
||||
<use xlink:href="#glyph0-18" x="1149.457031" y="58"/>
|
||||
<use xlink:href="#glyph0-7" x="1154.162109" y="58"/>
|
||||
<use xlink:href="#glyph0-2" x="1159.095703" y="58"/>
|
||||
<use xlink:href="#glyph0-3" x="1166.449219" y="58"/>
|
||||
<use xlink:href="#glyph0-19" x="1173.046875" y="58"/>
|
||||
<use xlink:href="#glyph0-20" x="1180.429688" y="58"/>
|
||||
<use xlink:href="#glyph0-13" x="1183.763672" y="58"/>
|
||||
</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%,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 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(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(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%,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 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 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 style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||
<use xlink:href="#glyph0-18" x="812.351562" y="58"/>
|
||||
<use xlink:href="#glyph0-7" x="817.056641" y="58"/>
|
||||
<use xlink:href="#glyph0-2" x="821.990234" y="58"/>
|
||||
<use xlink:href="#glyph0-3" x="829.34375" y="58"/>
|
||||
<use xlink:href="#glyph0-19" x="835.941406" y="58"/>
|
||||
<use xlink:href="#glyph0-20" x="843.324219" y="58"/>
|
||||
<use xlink:href="#glyph0-13" x="846.658203" y="58"/>
|
||||
<use xlink:href="#glyph0-18" x="168.484375" y="90"/>
|
||||
<use xlink:href="#glyph0-7" x="173.189453" y="90"/>
|
||||
<use xlink:href="#glyph0-2" x="178.123047" y="90"/>
|
||||
<use xlink:href="#glyph0-3" x="185.476562" y="90"/>
|
||||
<use xlink:href="#glyph0-19" x="192.074219" y="90"/>
|
||||
<use xlink:href="#glyph0-20" x="199.457031" y="90"/>
|
||||
<use xlink:href="#glyph0-13" x="202.791016" y="90"/>
|
||||
</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%,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 "/>
|
||||
<g style="fill:rgb(100%,100%,100%);fill-opacity:1;">
|
||||
<use xlink:href="#glyph0-21" x="854.671875" y="58"/>
|
||||
<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 "/>
|
||||
<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 483.074219 78 L 932.367188 78 L 932.367188 92 L 483.074219 92 Z M 483.074219 78 "/>
|
||||
<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 "/>
|
||||
<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 "/>
|
||||
<g clip-path="url(#clip3)" clip-rule="nonzero">
|
||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||
<use xlink:href="#glyph0-18" x="1068.0625" y="58"/>
|
||||
<use xlink:href="#glyph0-7" x="1072.767578" y="58"/>
|
||||
<use xlink:href="#glyph0-2" x="1077.701172" y="58"/>
|
||||
<use xlink:href="#glyph0-3" x="1085.054688" y="58"/>
|
||||
<use xlink:href="#glyph0-19" x="1091.652344" y="58"/>
|
||||
<use xlink:href="#glyph0-20" x="1099.035156" y="58"/>
|
||||
<use xlink:href="#glyph0-13" x="1102.369141" y="58"/>
|
||||
<use xlink:href="#glyph0-18" x="945.597656" y="90"/>
|
||||
<use xlink:href="#glyph0-7" x="950.302734" y="90"/>
|
||||
<use xlink:href="#glyph0-2" x="955.236328" y="90"/>
|
||||
<use xlink:href="#glyph0-3" x="962.589844" y="90"/>
|
||||
<use xlink:href="#glyph0-19" x="969.1875" y="90"/>
|
||||
<use xlink:href="#glyph0-20" x="976.570312" y="90"/>
|
||||
<use xlink:href="#glyph0-13" x="979.904297" y="90"/>
|
||||
</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 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%,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=" 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 "/>
|
||||
<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%,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="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 "/>
|
||||
<g clip-path="url(#clip4)" clip-rule="nonzero">
|
||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||
<use xlink:href="#glyph0-18" x="865.34375" y="90"/>
|
||||
<use xlink:href="#glyph0-7" x="870.048828" y="90"/>
|
||||
<use xlink:href="#glyph0-2" x="874.982422" y="90"/>
|
||||
<use xlink:href="#glyph0-3" x="882.335938" y="90"/>
|
||||
<use xlink:href="#glyph0-19" x="888.933594" y="90"/>
|
||||
<use xlink:href="#glyph0-20" x="896.316406" y="90"/>
|
||||
<use xlink:href="#glyph0-13" x="899.650391" y="90"/>
|
||||
<use xlink:href="#glyph1-1" x="250.011719" y="76"/>
|
||||
<use xlink:href="#glyph1-2" x="252.371094" y="76"/>
|
||||
<use xlink:href="#glyph1-3" x="257.265625" y="76"/>
|
||||
<use xlink:href="#glyph1-4" x="262.34375" y="76"/>
|
||||
<use xlink:href="#glyph1-5" x="264.886719" y="76"/>
|
||||
<use xlink:href="#glyph1-4" x="269.976562" y="76"/>
|
||||
<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>
|
||||
<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=" 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 "/>
|
||||
<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 "/>
|
||||
<g clip-path="url(#clip5)" clip-rule="nonzero">
|
||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||
<use xlink:href="#glyph0-18" x="951.027344" y="90"/>
|
||||
<use xlink:href="#glyph0-7" x="955.732422" y="90"/>
|
||||
<use xlink:href="#glyph0-2" x="960.666016" y="90"/>
|
||||
<use xlink:href="#glyph0-3" x="968.019531" y="90"/>
|
||||
<use xlink:href="#glyph0-19" x="974.617188" y="90"/>
|
||||
<use xlink:href="#glyph0-20" x="982" y="90"/>
|
||||
<use xlink:href="#glyph0-13" x="985.333984" y="90"/>
|
||||
<use xlink:href="#glyph1-5" x="979.003906" y="76"/>
|
||||
<use xlink:href="#glyph1-4" x="984.09375" y="76"/>
|
||||
<use xlink:href="#glyph1-13" x="986.636719" y="76"/>
|
||||
<use xlink:href="#glyph1-2" x="991.714844" y="76"/>
|
||||
<use xlink:href="#glyph1-11" x="996.609375" y="76"/>
|
||||
<use xlink:href="#glyph1-14" x="1001.679688" y="76"/>
|
||||
</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=" 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 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%,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="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 "/>
|
||||
<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%,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 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(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 "/>
|
||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||
<use xlink:href="#glyph1-1" x="432.597656" y="76"/>
|
||||
<use xlink:href="#glyph1-2" x="434.820312" y="76"/>
|
||||
<use xlink:href="#glyph1-3" x="437.363281" y="76"/>
|
||||
<use xlink:href="#glyph1-2" x="444.066406" y="76"/>
|
||||
<use xlink:href="#glyph1-4" x="446.609375" y="76"/>
|
||||
<use xlink:href="#glyph0-18" x="546.976562" y="122"/>
|
||||
<use xlink:href="#glyph0-7" x="551.681641" y="122"/>
|
||||
<use xlink:href="#glyph0-2" x="556.615234" y="122"/>
|
||||
<use xlink:href="#glyph0-3" x="563.96875" y="122"/>
|
||||
<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>
|
||||
<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 "/>
|
||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||
<use xlink:href="#glyph1-1" x="888.390625" y="76"/>
|
||||
<use xlink:href="#glyph1-2" x="890.613281" y="76"/>
|
||||
<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 "/>
|
||||
<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 "/>
|
||||
<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 "/>
|
||||
<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 "/>
|
||||
<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 "/>
|
||||
<g clip-path="url(#clip6)" clip-rule="nonzero">
|
||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||
<use xlink:href="#glyph0-18" x="764.328125" y="122"/>
|
||||
<use xlink:href="#glyph0-7" x="769.033203" y="122"/>
|
||||
<use xlink:href="#glyph0-2" x="773.966797" y="122"/>
|
||||
<use xlink:href="#glyph0-3" x="781.320312" y="122"/>
|
||||
<use xlink:href="#glyph0-19" x="787.917969" y="122"/>
|
||||
<use xlink:href="#glyph0-20" x="795.300781" y="122"/>
|
||||
<use xlink:href="#glyph0-13" x="798.634766" y="122"/>
|
||||
<use xlink:href="#glyph0-18" x="1018.117188" y="122"/>
|
||||
<use xlink:href="#glyph0-7" x="1022.822266" y="122"/>
|
||||
<use xlink:href="#glyph0-2" x="1027.755859" y="122"/>
|
||||
<use xlink:href="#glyph0-3" x="1035.109375" y="122"/>
|
||||
<use xlink:href="#glyph0-19" x="1041.707031" y="122"/>
|
||||
<use xlink:href="#glyph0-20" x="1049.089844" y="122"/>
|
||||
<use xlink:href="#glyph0-13" x="1052.423828" y="122"/>
|
||||
</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%,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%,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=" 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 "/>
|
||||
<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 1071.8125 110 L 1070.394531 110 L 1070.394531 124 L 1071.8125 124 Z M 1071.8125 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 "/>
|
||||
<g clip-path="url(#clip7)" clip-rule="nonzero">
|
||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||
<use xlink:href="#glyph0-18" x="909.84375" y="122"/>
|
||||
<use xlink:href="#glyph0-7" x="914.548828" y="122"/>
|
||||
<use xlink:href="#glyph0-2" x="919.482422" y="122"/>
|
||||
<use xlink:href="#glyph0-3" x="926.835938" y="122"/>
|
||||
<use xlink:href="#glyph0-19" x="933.433594" y="122"/>
|
||||
<use xlink:href="#glyph0-20" x="940.816406" y="122"/>
|
||||
<use xlink:href="#glyph0-13" x="944.150391" y="122"/>
|
||||
<use xlink:href="#glyph1-1" x="583.375" y="108"/>
|
||||
<use xlink:href="#glyph1-2" x="585.734375" y="108"/>
|
||||
<use xlink:href="#glyph1-3" x="590.628906" y="108"/>
|
||||
<use xlink:href="#glyph1-4" x="595.707031" y="108"/>
|
||||
<use xlink:href="#glyph1-15" x="598.25" y="108"/>
|
||||
<use xlink:href="#glyph1-4" x="603.339844" y="108"/>
|
||||
<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>
|
||||
<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=" 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 "/>
|
||||
<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 "/>
|
||||
<g clip-path="url(#clip8)" clip-rule="nonzero">
|
||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||
<use xlink:href="#glyph0-18" x="1007.089844" y="122"/>
|
||||
<use xlink:href="#glyph0-7" x="1011.794922" y="122"/>
|
||||
<use xlink:href="#glyph0-2" x="1016.728516" y="122"/>
|
||||
<use xlink:href="#glyph0-3" x="1024.082031" y="122"/>
|
||||
<use xlink:href="#glyph0-19" x="1030.679688" y="122"/>
|
||||
<use xlink:href="#glyph0-20" x="1038.0625" y="122"/>
|
||||
<use xlink:href="#glyph0-13" x="1041.396484" y="122"/>
|
||||
<use xlink:href="#glyph1-15" x="1049.300781" y="108"/>
|
||||
<use xlink:href="#glyph1-4" x="1054.390625" y="108"/>
|
||||
<use xlink:href="#glyph1-13" x="1056.933594" y="108"/>
|
||||
<use xlink:href="#glyph1-2" x="1062.011719" y="108"/>
|
||||
<use xlink:href="#glyph1-11" x="1066.90625" y="108"/>
|
||||
<use xlink:href="#glyph1-14" x="1071.976562" y="108"/>
|
||||
</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=" 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 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%,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="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 "/>
|
||||
<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%,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 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(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 "/>
|
||||
<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 style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||
<use xlink:href="#glyph1-18" x="789.792969" y="108"/>
|
||||
<use xlink:href="#glyph1-2" x="792.015625" y="108"/>
|
||||
<use xlink:href="#glyph1-3" x="794.558594" y="108"/>
|
||||
<use xlink:href="#glyph1-2" x="801.261719" y="108"/>
|
||||
<use xlink:href="#glyph1-4" x="803.804688" y="108"/>
|
||||
<use xlink:href="#glyph0-18" x="1082.304688" y="154"/>
|
||||
<use xlink:href="#glyph0-7" x="1087.009766" y="154"/>
|
||||
<use xlink:href="#glyph0-2" x="1091.943359" y="154"/>
|
||||
<use xlink:href="#glyph0-3" x="1099.296875" y="154"/>
|
||||
<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>
|
||||
<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 style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||
<use xlink:href="#glyph1-18" x="931.160156" y="108"/>
|
||||
<use xlink:href="#glyph1-2" x="933.382812" y="108"/>
|
||||
<use xlink:href="#glyph1-3" x="935.925781" y="108"/>
|
||||
<use xlink:href="#glyph1-2" x="942.628906" y="108"/>
|
||||
<use xlink:href="#glyph1-5" x="945.171875" y="108"/>
|
||||
<use xlink:href="#glyph1-1" x="819.433594" y="140"/>
|
||||
<use xlink:href="#glyph1-2" x="821.792969" y="140"/>
|
||||
<use xlink:href="#glyph1-3" x="826.6875" y="140"/>
|
||||
<use xlink:href="#glyph1-4" x="831.765625" y="140"/>
|
||||
<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>
|
||||
<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 style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||
<use xlink:href="#glyph1-18" x="1013.980469" y="108"/>
|
||||
<use xlink:href="#glyph1-2" x="1016.203125" y="108"/>
|
||||
<use xlink:href="#glyph1-3" x="1018.746094" y="108"/>
|
||||
<use xlink:href="#glyph1-2" x="1025.449219" y="108"/>
|
||||
<use xlink:href="#glyph1-6" x="1027.992188" y="108"/>
|
||||
<use xlink:href="#glyph1-16" x="1111.605469" y="140"/>
|
||||
<use xlink:href="#glyph1-4" x="1116.695312" y="140"/>
|
||||
<use xlink:href="#glyph1-13" x="1119.238281" y="140"/>
|
||||
<use xlink:href="#glyph1-2" x="1124.316406" y="140"/>
|
||||
<use xlink:href="#glyph1-11" x="1129.210938" y="140"/>
|
||||
<use xlink:href="#glyph1-14" x="1134.28125" y="140"/>
|
||||
</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(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(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 899.039062 49 L 899.039062 43 "/>
|
||||
<g clip-path="url(#clip12)" clip-rule="nonzero">
|
||||
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||
<use xlink:href="#glyph1-19" x="836.167969" y="44"/>
|
||||
<use xlink:href="#glyph1-12" x="841.246094" y="44"/>
|
||||
<use xlink:href="#glyph1-20" x="846.167969" y="44"/>
|
||||
<use xlink:href="#glyph1-16" x="850.566406" y="44"/>
|
||||
<use xlink:href="#glyph1-21" x="855.460938" y="44"/>
|
||||
<use xlink:href="#glyph1-14" x="860.53125" y="44"/>
|
||||
<use xlink:href="#glyph1-2" x="865.609375" y="44"/>
|
||||
<use xlink:href="#glyph1-10" x="868.152344" y="44"/>
|
||||
<use xlink:href="#glyph1-11" x="871.289062" y="44"/>
|
||||
<use xlink:href="#glyph1-8" x="876.359375" y="44"/>
|
||||
<use xlink:href="#glyph1-12" x="879.648438" y="44"/>
|
||||
<use xlink:href="#glyph1-13" x="884.570312" y="44"/>
|
||||
<use xlink:href="#glyph1-14" x="889.472656" y="44"/>
|
||||
<use xlink:href="#glyph1-2" x="894.550781" y="44"/>
|
||||
<use xlink:href="#glyph1-15" x="897.09375" y="44"/>
|
||||
<use xlink:href="#glyph1-16" x="899.910156" y="44"/>
|
||||
<use xlink:href="#glyph1-8" x="904.804688" y="44"/>
|
||||
<use xlink:href="#glyph1-17" x="908.09375" y="44"/>
|
||||
<use xlink:href="#glyph1-12" x="912.726562" y="44"/>
|
||||
<use xlink:href="#glyph1-14" x="917.648438" y="44"/>
|
||||
<use xlink:href="#glyph1-22" x="922.726562" y="44"/>
|
||||
<use xlink:href="#glyph1-2" x="925.421875" y="44"/>
|
||||
<use xlink:href="#glyph1-10" x="927.964844" 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"/>
|
||||
<use xlink:href="#glyph1-17" x="901.039062" y="44"/>
|
||||
<use xlink:href="#glyph1-18" x="906.511719" y="44"/>
|
||||
<use xlink:href="#glyph1-18" x="908.734375" y="44"/>
|
||||
<use xlink:href="#glyph1-4" x="910.957031" y="44"/>
|
||||
<use xlink:href="#glyph1-19" x="913.5" y="44"/>
|
||||
<use xlink:href="#glyph1-20" x="917.898438" y="44"/>
|
||||
<use xlink:href="#glyph1-10" x="922.96875" y="44"/>
|
||||
<use xlink:href="#glyph1-18" x="925.191406" y="44"/>
|
||||
<use xlink:href="#glyph1-13" x="927.414062" y="44"/>
|
||||
<use xlink:href="#glyph1-4" x="932.492188" y="44"/>
|
||||
<use xlink:href="#glyph1-21" x="935.035156" y="44"/>
|
||||
<use xlink:href="#glyph1-10" x="937.851562" y="44"/>
|
||||
<use xlink:href="#glyph1-3" x="940.074219" y="44"/>
|
||||
<use xlink:href="#glyph1-14" x="945.152344" y="44"/>
|
||||
<use xlink:href="#glyph1-9" x="950.074219" y="44"/>
|
||||
<use xlink:href="#glyph1-6" x="953.363281" y="44"/>
|
||||
<use xlink:href="#glyph1-4" x="957.53125" y="44"/>
|
||||
<use xlink:href="#glyph1-21" x="960.074219" y="44"/>
|
||||
<use xlink:href="#glyph1-2" x="962.890625" y="44"/>
|
||||
<use xlink:href="#glyph1-9" x="967.785156" y="44"/>
|
||||
<use xlink:href="#glyph1-22" x="971.074219" y="44"/>
|
||||
<use xlink:href="#glyph1-14" x="975.707031" y="44"/>
|
||||
<use xlink:href="#glyph1-13" x="980.628906" 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 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 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 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 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;">
|
||||
<use xlink:href="#glyph1-9" x="33.601562" y="44"/>
|
||||
<use xlink:href="#glyph1-27" x="37.769531" y="44"/>
|
||||
<use xlink:href="#glyph1-1" x="44.3125" y="44"/>
|
||||
<use xlink:href="#glyph1-10" x="46.535156" y="44"/>
|
||||
<use xlink:href="#glyph1-20" x="49.671875" y="44"/>
|
||||
<use xlink:href="#glyph1-11" x="54.070312" y="44"/>
|
||||
<use xlink:href="#glyph1-6" x="44.675781" y="44"/>
|
||||
<use xlink:href="#glyph1-23" x="48.84375" y="44"/>
|
||||
<use xlink:href="#glyph1-10" x="55.386719" y="44"/>
|
||||
<use xlink:href="#glyph1-7" x="57.609375" y="44"/>
|
||||
<use xlink:href="#glyph1-19" x="60.746094" y="44"/>
|
||||
<use xlink:href="#glyph1-20" x="65.144531" y="44"/>
|
||||
</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 style="fill:rgb(0%,0%,0%);fill-opacity:1;">
|
||||
<use xlink:href="#glyph1-19" x="1088.320312" y="44"/>
|
||||
<use xlink:href="#glyph1-27" x="1093.398438" y="44"/>
|
||||
<use xlink:href="#glyph1-1" x="1099.941406" y="44"/>
|
||||
<use xlink:href="#glyph1-10" x="1102.164062" y="44"/>
|
||||
<use xlink:href="#glyph1-20" x="1105.300781" y="44"/>
|
||||
<use xlink:href="#glyph1-11" x="1109.699219" y="44"/>
|
||||
<use xlink:href="#glyph1-2" x="1114.769531" y="44"/>
|
||||
<use xlink:href="#glyph1-1" x="1117.3125" y="44"/>
|
||||
<use xlink:href="#glyph1-9" x="1119.535156" y="44"/>
|
||||
<use xlink:href="#glyph1-2" x="1123.703125" y="44"/>
|
||||
<use xlink:href="#glyph1-15" x="1126.246094" y="44"/>
|
||||
<use xlink:href="#glyph1-1" x="1129.0625" y="44"/>
|
||||
<use xlink:href="#glyph1-21" x="1131.285156" y="44"/>
|
||||
<use xlink:href="#glyph1-1" x="1136.355469" y="44"/>
|
||||
<use xlink:href="#glyph1-9" x="1138.578125" y="44"/>
|
||||
<use xlink:href="#glyph1-11" x="1142.746094" y="44"/>
|
||||
<use xlink:href="#glyph1-12" x="1147.816406" y="44"/>
|
||||
<use xlink:href="#glyph1-14" x="1152.738281" y="44"/>
|
||||
<use xlink:href="#glyph1-24" x="1179.296875" y="44"/>
|
||||
<use xlink:href="#glyph1-23" x="1184.375" y="44"/>
|
||||
<use xlink:href="#glyph1-10" x="1190.917969" y="44"/>
|
||||
<use xlink:href="#glyph1-7" x="1193.140625" y="44"/>
|
||||
<use xlink:href="#glyph1-19" x="1196.277344" y="44"/>
|
||||
<use xlink:href="#glyph1-20" x="1200.675781" y="44"/>
|
||||
<use xlink:href="#glyph1-4" x="1205.746094" y="44"/>
|
||||
<use xlink:href="#glyph1-10" x="1208.289062" y="44"/>
|
||||
<use xlink:href="#glyph1-6" x="1210.511719" y="44"/>
|
||||
<use xlink:href="#glyph1-4" x="1214.679688" y="44"/>
|
||||
<use xlink:href="#glyph1-21" x="1217.222656" y="44"/>
|
||||
<use xlink:href="#glyph1-10" x="1220.039062" y="44"/>
|
||||
<use xlink:href="#glyph1-11" x="1222.261719" y="44"/>
|
||||
<use xlink:href="#glyph1-10" x="1227.332031" y="44"/>
|
||||
<use xlink:href="#glyph1-6" x="1229.554688" y="44"/>
|
||||
<use xlink:href="#glyph1-20" x="1233.722656" y="44"/>
|
||||
<use xlink:href="#glyph1-14" x="1238.792969" y="44"/>
|
||||
<use xlink:href="#glyph1-13" x="1243.714844" y="44"/>
|
||||
</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>
|
||||
</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)
|
||||
(name eio)
|
||||
(formatting disabled)
|
||||
(subst disabled)
|
||||
(generate_opam_files true)
|
||||
(source (github ocaml-multicore/eio))
|
||||
(license ISC)
|
||||
@ -13,7 +14,7 @@
|
||||
(description "An effect-based IO API for multicore OCaml with fibers.")
|
||||
(conflicts (seq (< 0.3)))
|
||||
(depends
|
||||
(ocaml (>= 5.1.0))
|
||||
(ocaml (>= 5.2.0))
|
||||
(bigstringaf (>= 0.9.0))
|
||||
(cstruct (>= 6.0.1))
|
||||
lwt-dllist
|
||||
@ -24,7 +25,7 @@
|
||||
(domain-local-await (>= 0.1.0))
|
||||
(crowbar (and (>= 0.2) :with-test))
|
||||
(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))))
|
||||
(package
|
||||
(name eio_linux)
|
||||
@ -34,11 +35,11 @@
|
||||
(depends
|
||||
(alcotest (and (>= 1.7.0) :with-test))
|
||||
(eio (= :version))
|
||||
(mdx (and (>= 2.2.0) :with-test))
|
||||
(mdx (and (>= 2.4.1) :with-test))
|
||||
(logs (and (>= 0.7.0) :with-test))
|
||||
(fmt (>= 0.8.9))
|
||||
(cmdliner (and (>= 1.1.0) :with-test))
|
||||
(uring (>= 0.7))))
|
||||
(uring (>= 0.9))))
|
||||
(package
|
||||
(name eio_posix)
|
||||
(allow_empty) ; Work-around for dune bug #6938
|
||||
@ -47,7 +48,8 @@
|
||||
(depends
|
||||
(eio (= :version))
|
||||
(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))))
|
||||
(package
|
||||
(name eio_windows)
|
||||
@ -64,7 +66,7 @@
|
||||
(synopsis "Effect-based direct-style IO mainloop for OCaml")
|
||||
(description "Selects an appropriate Eio backend for the current platform.")
|
||||
(depends
|
||||
(mdx (and (>= 2.2.0) :with-test))
|
||||
(mdx (and (>= 2.4.1) :with-test))
|
||||
(kcas (and (>= 0.3.0) :with-test))
|
||||
(yojson (and (>= 2.0.2) :with-test))
|
||||
(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"
|
||||
depends: [
|
||||
"dune" {>= "3.9"}
|
||||
"ocaml" {>= "5.1.0"}
|
||||
"ocaml" {>= "5.2.0"}
|
||||
"bigstringaf" {>= "0.9.0"}
|
||||
"cstruct" {>= "6.0.1"}
|
||||
"lwt-dllist"
|
||||
@ -21,7 +21,7 @@ depends: [
|
||||
"domain-local-await" {>= "0.1.0"}
|
||||
"crowbar" {>= "0.2" & with-test}
|
||||
"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}
|
||||
"odoc" {with-doc}
|
||||
]
|
||||
@ -29,7 +29,6 @@ conflicts: [
|
||||
"seq" {< "0.3"}
|
||||
]
|
||||
build: [
|
||||
["dune" "subst"] {dev}
|
||||
[
|
||||
"dune"
|
||||
"build"
|
||||
|
@ -12,15 +12,14 @@ depends: [
|
||||
"dune" {>= "3.9"}
|
||||
"alcotest" {>= "1.7.0" & with-test}
|
||||
"eio" {= version}
|
||||
"mdx" {>= "2.2.0" & with-test}
|
||||
"mdx" {>= "2.4.1" & with-test}
|
||||
"logs" {>= "0.7.0" & with-test}
|
||||
"fmt" {>= "0.8.9"}
|
||||
"cmdliner" {>= "1.1.0" & with-test}
|
||||
"uring" {>= "0.7"}
|
||||
"uring" {>= "0.9"}
|
||||
"odoc" {with-doc}
|
||||
]
|
||||
build: [
|
||||
["dune" "subst"] {dev}
|
||||
[
|
||||
"dune"
|
||||
"build"
|
||||
|
@ -10,7 +10,7 @@ doc: "https://ocaml-multicore.github.io/eio/"
|
||||
bug-reports: "https://github.com/ocaml-multicore/eio/issues"
|
||||
depends: [
|
||||
"dune" {>= "3.9"}
|
||||
"mdx" {>= "2.2.0" & with-test}
|
||||
"mdx" {>= "2.4.1" & with-test}
|
||||
"kcas" {>= "0.3.0" & with-test}
|
||||
"yojson" {>= "2.0.2" & with-test}
|
||||
"eio_linux"
|
||||
@ -21,7 +21,6 @@ depends: [
|
||||
"odoc" {with-doc}
|
||||
]
|
||||
build: [
|
||||
["dune" "subst"] {dev}
|
||||
[
|
||||
"dune"
|
||||
"build"
|
||||
|
@ -12,12 +12,12 @@ depends: [
|
||||
"dune" {>= "3.9"}
|
||||
"eio" {= version}
|
||||
"iomux" {>= "0.2"}
|
||||
"mdx" {>= "2.2.0" & with-test}
|
||||
"mdx" {>= "2.4.1" & with-test}
|
||||
"conf-bash" {with-test}
|
||||
"fmt" {>= "0.8.9"}
|
||||
"odoc" {with-doc}
|
||||
]
|
||||
build: [
|
||||
["dune" "subst"] {dev}
|
||||
[
|
||||
"dune"
|
||||
"build"
|
||||
|
@ -17,7 +17,6 @@ depends: [
|
||||
"odoc" {with-doc}
|
||||
]
|
||||
build: [
|
||||
["dune" "subst"] {dev}
|
||||
[
|
||||
"dune"
|
||||
"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
|
||||
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.
|
||||
The sequence ends if this returns [true].
|
||||
The default is {!at_end_of_input}. *)
|
||||
|
@ -1,4 +1,4 @@
|
||||
(library
|
||||
(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 Cells = Cells
|
||||
module Broadcast = Broadcast
|
||||
module Single_waiter = Single_waiter
|
||||
module Trace = Trace
|
||||
module Fiber_context = Cancel.Fiber_context
|
||||
module Debug = Debug
|
||||
@ -18,6 +19,4 @@ module Private = struct
|
||||
| Fork = Fiber.Fork
|
||||
| Get_context = Cancel.Get_context
|
||||
end
|
||||
|
||||
module Dla = Dla
|
||||
end
|
||||
|
@ -606,6 +606,7 @@ module Private : sig
|
||||
|
||||
module Cells = Cells
|
||||
module Broadcast = Broadcast
|
||||
module Single_waiter = Single_waiter
|
||||
|
||||
(** Every fiber has an associated context. *)
|
||||
module Fiber_context : sig
|
||||
@ -781,8 +782,4 @@ module Private : sig
|
||||
val v : t
|
||||
(** Backends should use this for {!Eio.Stdenv.debug}. *)
|
||||
end
|
||||
|
||||
module Dla : sig
|
||||
val prepare_for_await : unit -> Domain_local_await.t
|
||||
end
|
||||
end
|
||||
|
@ -39,7 +39,8 @@ let fork_daemon ~sw f =
|
||||
(* The daemon was cancelled because all non-daemon fibers are finished. *)
|
||||
()
|
||||
| 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 *)
|
||||
|
||||
let fork_promise ~sw f =
|
||||
@ -65,7 +66,8 @@ let fork_promise_exn ~sw f =
|
||||
match Switch.with_op sw f with
|
||||
| x -> Promise.resolve r x
|
||||
| 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
|
||||
|
||||
@ -226,7 +228,7 @@ module List = struct
|
||||
|
||||
let release t =
|
||||
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 =
|
||||
await_free t;
|
||||
|
@ -1,25 +1,32 @@
|
||||
(* 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 state =
|
||||
| Running
|
||||
| Sleeping of (('a, exn) result -> unit)
|
||||
|
||||
type 'a t = {
|
||||
mutable wake : ('a, exn) result -> unit;
|
||||
}
|
||||
type 'a t = 'a state ref
|
||||
|
||||
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 x =
|
||||
Suspend.enter op @@ fun ctx enqueue ->
|
||||
Cancel.Fiber_context.set_cancel_fn ctx (fun ex ->
|
||||
t.wake <- ignore;
|
||||
t := Running;
|
||||
enqueue (Error ex)
|
||||
);
|
||||
t.wake <- (fun x ->
|
||||
t := Sleeping (fun x ->
|
||||
Cancel.Fiber_context.clear_cancel_fn ctx;
|
||||
t.wake <- ignore;
|
||||
t := Running;
|
||||
enqueue x
|
||||
)
|
||||
in
|
||||
@ -29,7 +36,7 @@ let await t op id =
|
||||
let await_protect t op id =
|
||||
let x =
|
||||
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
|
||||
Trace.get id;
|
||||
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
|
||||
Cancel.cancel t.cancel Exit;
|
||||
if t.fibers = 0 then
|
||||
Single_waiter.wake t.waiter (Ok ())
|
||||
Single_waiter.wake_if_sleeping t.waiter
|
||||
|
||||
let with_op t fn =
|
||||
inc_fibers t;
|
||||
|
@ -70,6 +70,7 @@ module Pi = struct
|
||||
val rmdir : t -> path -> unit
|
||||
val rename : t -> path -> _ dir -> path -> unit
|
||||
val read_link : t -> path -> string
|
||||
val symlink : link_to:path -> t -> path -> unit
|
||||
val pp : t Fmt.t
|
||||
val native : t -> string -> string option
|
||||
end
|
||||
|
@ -105,7 +105,7 @@ let run_full main =
|
||||
let result = ref None in
|
||||
let `Exit_scheduler =
|
||||
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 () ->
|
||||
fork ~new_fiber (fun () -> result := Some (main stdenv))) in
|
||||
match !result with
|
||||
|
@ -231,6 +231,9 @@ val run_server :
|
||||
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.
|
||||
|
||||
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.
|
||||
The default is [Int.max_int].
|
||||
@param stop Resolving this promise causes [s] to stop accepting new connections.
|
||||
|
@ -1,11 +1,22 @@
|
||||
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 =
|
||||
match p1, p2 with
|
||||
| p1, "" -> (dir, Filename.concat p1 p2)
|
||||
| _, p2 when not (Filename.is_relative p2) -> (dir, p2)
|
||||
| p1, "" -> (dir, concat p1 p2)
|
||||
| _, p2 when not (is_relative 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 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
|
||||
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 =
|
||||
(* Check parent exists first. *)
|
||||
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].
|
||||
|
||||
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"
|
||||
]} *)
|
||||
|
@ -19,7 +19,7 @@ type 'a slot = 'a option ref
|
||||
module Cell = struct
|
||||
(* The possible behaviours are:
|
||||
|
||||
1. Suspender : In_transition -> Request Suspender waits for a resource
|
||||
1. Suspender : In_transition -> Request Suspender waits for a resource
|
||||
1.1. Resumer : Request -> Finished Resumer then providers a resource
|
||||
1.2. Suspender : Request -> Finished Suspender cancels
|
||||
2. Resumer : In_transition -> Resource Resumer provides a spare resource
|
||||
@ -89,11 +89,10 @@ let cancel segment cell =
|
||||
| In_transition | Resource _ -> assert false (* Can't get here from [Request]. *)
|
||||
|
||||
(* If [t] is under capacity, add another (empty) slot. *)
|
||||
let rec maybe_add_slot t =
|
||||
let current = Atomic.get t.slots in
|
||||
let rec maybe_add_slot t current =
|
||||
if current < t.max_slots then (
|
||||
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.
|
||||
@ -114,7 +113,7 @@ let run_with t f slot =
|
||||
f x
|
||||
end
|
||||
with
|
||||
| r ->
|
||||
| r ->
|
||||
add t slot;
|
||||
r
|
||||
| exception ex ->
|
||||
@ -122,7 +121,19 @@ let run_with t f slot =
|
||||
add t slot;
|
||||
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
|
||||
match Atomic.get cell with
|
||||
| Finished | Request _ -> assert false
|
||||
@ -130,9 +141,18 @@ let use t f =
|
||||
Atomic.set cell Finished; (* Allow value to be GC'd *)
|
||||
run_with t f slot
|
||||
| In_transition ->
|
||||
(* It would have been better if more resources were available.
|
||||
If we still have capacity, add a new slot now. *)
|
||||
maybe_add_slot t;
|
||||
let current = Atomic.get t.slots in
|
||||
match current < t.max_slots with
|
||||
| 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 *)
|
||||
let slot =
|
||||
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,
|
||||
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].
|
||||
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
|
||||
|
||||
let make () = Atomic.make `Open
|
||||
|
@ -87,6 +87,11 @@ module Timeout = struct
|
||||
| Timeout (clock, d) ->
|
||||
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 =
|
||||
if d >= 0.001 && d < 0.1 then
|
||||
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],
|
||||
raising exception {!exception-Timeout}. *)
|
||||
|
||||
val sleep : t -> unit
|
||||
(** [sleep t] sleeps for [t]'s duration. *)
|
||||
|
||||
val pp : t Fmt.t
|
||||
(** [pp] formats a timeout as a duration (e.g. "5s").
|
||||
This is intended for use in error messages and logging and is rounded. *)
|
||||
|
@ -1,6 +1,7 @@
|
||||
(library
|
||||
(name eio_unix)
|
||||
(public_name eio.unix)
|
||||
(public_headers include/fork_action.h)
|
||||
(foreign_stubs
|
||||
(language c)
|
||||
(include_dirs include)
|
||||
|
@ -61,18 +61,23 @@ type t = [`Generic | `Unix] Eio.Net.ty r
|
||||
|
||||
type _ 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
|
||||
| Socketpair_stream : Switch.t * Unix.socket_domain * int ->
|
||||
([`Unix_fd | stream_socket_ty] r * [`Unix_fd | stream_socket_ty] r) Effect.t
|
||||
| Socketpair_datagram : Switch.t * Unix.socket_domain * int ->
|
||||
([`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_datagram s = (s : _ datagram_socket :> [< `Unix_fd | datagram_socket_ty] r)
|
||||
let open_stream s = (s : [`Unix_fd | stream_socket_ty] r :> [< `Unix_fd | stream_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 =
|
||||
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 =
|
||||
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)
|
||||
|
||||
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 =
|
||||
Option.get (Resource.fd_opt socket)
|
||||
|
@ -55,16 +55,23 @@ end
|
||||
|
||||
(** {2 Creating or importing sockets} *)
|
||||
|
||||
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].
|
||||
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 fd] is an Eio flow that uses [fd].
|
||||
|
||||
It can be cast to e.g. {!source} for a one-way flow.
|
||||
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:true fd] is an Eio datagram socket that uses [fd].
|
||||
val import_socket_listening : sw:Switch.t -> close_unix:bool -> Unix.file_descr -> [< `Unix_fd | listening_socket_ty] r
|
||||
(** [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.
|
||||
|
||||
@ -75,7 +82,7 @@ val socketpair_stream :
|
||||
?domain:Unix.socket_domain ->
|
||||
?protocol:int ->
|
||||
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.
|
||||
|
||||
This creates OS-level resources using [socketpair(2)].
|
||||
@ -86,7 +93,7 @@ val socketpair_datagram :
|
||||
?domain:Unix.socket_domain ->
|
||||
?protocol:int ->
|
||||
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.
|
||||
|
||||
This creates OS-level resources using [socketpair(2)].
|
||||
@ -100,6 +107,8 @@ val getnameinfo : Eio.Net.Sockaddr.t -> (string * string)
|
||||
type _ Effect.t +=
|
||||
| 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 :
|
||||
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 ->
|
||||
|
@ -98,7 +98,13 @@ let get t =
|
||||
None
|
||||
|
||||
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.
|
||||
However, that makes dscheck too slow. *)
|
||||
|
@ -7,10 +7,10 @@ let prepare_for_await () =
|
||||
| _ -> ()
|
||||
and await () =
|
||||
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
|
||||
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 (
|
||||
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
|
||||
(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 Suspended = Suspended
|
||||
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)
|
||||
| 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
|
||||
type tag = [`Generic | `Unix]
|
||||
@ -145,78 +69,6 @@ let datagram_handler = Eio_unix.Pi.datagram_handler (module Datagram_socket)
|
||||
let datagram_socket fd =
|
||||
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
|
||||
type t = Fd.t
|
||||
|
||||
@ -233,7 +85,7 @@ module Listening_socket = struct
|
||||
| Unix.ADDR_UNIX path -> `Unix path
|
||||
| Unix.ADDR_INET (host, port) -> `Tcp (Eio_unix.Net.Ipaddr.of_unix host, port)
|
||||
in
|
||||
let flow = (flow client :> _ Eio.Net.stream_socket) in
|
||||
let flow = (Flow.of_fd client :> _ Eio.Net.stream_socket) in
|
||||
flow, client_addr
|
||||
|
||||
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 = Fd.of_unix ~sw ~seekable:false ~close_unix:true sock_unix in
|
||||
Low_level.connect sock addr;
|
||||
(flow sock :> _ Eio_unix.Net.stream_socket)
|
||||
(Flow.of_fd sock :> _ Eio_unix.Net.stream_socket)
|
||||
|
||||
module Impl = struct
|
||||
type t = unit
|
||||
@ -495,7 +347,7 @@ end = struct
|
||||
~flags:Uring.Open_flags.cloexec
|
||||
~perm:0
|
||||
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 perm, flags =
|
||||
@ -511,7 +363,7 @@ end = struct
|
||||
~flags:Uring.Open_flags.(cloexec + flags)
|
||||
~perm
|
||||
in
|
||||
(flow fd :> Eio.File.rw_ty r)
|
||||
(Flow.of_fd fd :> Eio.File.rw_ty r)
|
||||
|
||||
let native_internal t path =
|
||||
if Filename.is_relative path then (
|
||||
@ -592,7 +444,7 @@ end = struct
|
||||
~flags:Uring.Open_flags.(cloexec + path + (if follow then empty else nofollow))
|
||||
~perm:0
|
||||
in
|
||||
Flow.stat fd
|
||||
Low_level.fstat fd
|
||||
)
|
||||
|
||||
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
|
||||
| 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 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)
|
||||
|
||||
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 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 cwd = (dir ~label:"cwd" ~path:"" Cwd, "") in
|
||||
object (_ : stdenv)
|
||||
method stdin = stdin
|
||||
method stdout = stdout
|
||||
method stderr = stderr
|
||||
method stdin = Flow.stdin
|
||||
method stdout = Flow.stdout
|
||||
method stderr = Flow.stderr
|
||||
method net = net
|
||||
method process_mgr = process_mgr
|
||||
method domain_mgr = domain_mgr ~run_event_loop
|
||||
@ -646,7 +488,7 @@ let stdenv ~run_event_loop =
|
||||
method mono_clock = mono_clock
|
||||
method fs = (fs :> 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 backend_id = "linux"
|
||||
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.Net.Import_socket_stream (sw, close_unix, fd) -> Some (fun k ->
|
||||
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 ->
|
||||
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 ->
|
||||
match
|
||||
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 b = Fd.of_unix ~sw ~seekable:false ~close_unix:true b |> 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.of_fd in
|
||||
((a :> _ Eio_unix.Net.stream_socket), (b :> _ Eio_unix.Net.stream_socket))
|
||||
with
|
||||
| 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 ->
|
||||
match
|
||||
let r, w = Low_level.pipe ~sw in
|
||||
let r = (flow r :> _ Eio_unix.source) in
|
||||
let w = (flow w :> _ Eio_unix.sink) in
|
||||
let r = (Flow.of_fd r :> _ Eio_unix.source) in
|
||||
let w = (Flow.of_fd w :> _ Eio_unix.sink) in
|
||||
(r, w)
|
||||
with
|
||||
| r -> continue k r
|
||||
|
@ -39,7 +39,11 @@
|
||||
#ifndef SYS_clone3
|
||||
# define SYS_clone3 435
|
||||
# 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 pidfd;
|
||||
uint64_t child_tid;
|
||||
@ -48,11 +52,7 @@ struct clone_args {
|
||||
uint64_t stack;
|
||||
uint64_t stack_size;
|
||||
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.
|
||||
#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);
|
||||
}
|
||||
|
||||
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) {
|
||||
CAMLparam1(v_ba);
|
||||
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.
|
||||
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;
|
||||
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)
|
||||
return child_pid; /* Success! */
|
||||
@ -198,7 +216,7 @@ CAMLprim value caml_eio_clone3(value v_errors, value v_actions) {
|
||||
CAMLlocal1(v_result);
|
||||
pid_t child_pid;
|
||||
int pidfd = -1; /* Is automatically close-on-exec */
|
||||
struct clone_args cl_args = {
|
||||
struct caml_eio_clone_args cl_args = {
|
||||
.flags = CLONE_PIDFD,
|
||||
.pidfd = (uintptr_t) &pidfd,
|
||||
.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" ""
|
||||
)
|
||||
|
||||
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 =
|
||||
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_symlinkat : string -> Unix.file_descr -> string -> unit = "caml_eio_symlinkat"
|
||||
|
||||
external eio_getrandom : Cstruct.buffer -> int -> int -> int = "caml_eio_getrandom"
|
||||
|
||||
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
|
||||
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 =
|
||||
try
|
||||
Fd.use_exn "shutdown" socket @@ fun fd ->
|
||||
@ -563,7 +599,10 @@ module Process = struct
|
||||
let exit_status, set_exit_status = Promise.create () in
|
||||
let t =
|
||||
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
|
||||
{ pid; pid_fd; exit_status }
|
||||
in
|
||||
|
@ -150,6 +150,9 @@ val unlink : rmdir:bool -> 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]. *)
|
||||
|
||||
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
|
||||
(** [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_mkdirat(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_getdents(value);
|
||||
CAMLprim value caml_eio_clone3(value, value);
|
||||
|
@ -50,7 +50,7 @@ type t = {
|
||||
uring: io_job Uring.t;
|
||||
mem: Uring.Region.t option;
|
||||
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. *)
|
||||
run_q : runnable Lf_queue.t;
|
||||
@ -74,9 +74,9 @@ type t = {
|
||||
type _ Effect.t +=
|
||||
| Enter : (t -> 'a Suspended.t -> unit) -> 'a Effect.t
|
||||
| Cancel : io_job Uring.job -> unit Effect.t
|
||||
| Alloc : Uring.Region.chunk option Effect.t
|
||||
| Alloc_or_wait : Uring.Region.chunk Effect.t
|
||||
| Free : Uring.Region.chunk -> unit Effect.t
|
||||
| Get : t Effect.t
|
||||
|
||||
let get () = Effect.perform Get
|
||||
|
||||
let wake_buffer =
|
||||
let b = Bytes.create 8 in
|
||||
@ -113,7 +113,10 @@ let enter op fn =
|
||||
Effect.perform (Enter fn)
|
||||
|
||||
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 =
|
||||
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 *)
|
||||
handle_complete st ~runnable result
|
||||
| None ->
|
||||
ignore (submit uring : int);
|
||||
let timeout =
|
||||
match next_due with
|
||||
| `Wait_until time ->
|
||||
@ -239,12 +241,13 @@ let rec schedule ({run_q; sleep_q; mem_q; uring; _} as st) : [`Exit_scheduler] =
|
||||
| `Nothing -> None
|
||||
in
|
||||
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 *)
|
||||
schedule st
|
||||
) else if timeout = None && Uring.active_ops uring = 0 then (
|
||||
(* Nothing further can happen at this point.
|
||||
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 *)
|
||||
`Exit_scheduler
|
||||
) 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 = false], a wake-up event will arrive and wake us up soon. *)
|
||||
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;
|
||||
Atomic.set st.need_wakeup false;
|
||||
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 (
|
||||
(* 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. *)
|
||||
ignore (submit uring : int);
|
||||
Atomic.set st.need_wakeup false;
|
||||
Lf_queue.push run_q IO; (* Re-inject IO job in the run queue *)
|
||||
schedule st
|
||||
@ -328,21 +339,6 @@ and complete_rw_req st ({len; cur_off; action; _} as req) res =
|
||||
| _, Exactly len -> Suspended.continue action len
|
||||
| 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 =
|
||||
Trace.log "poll_add";
|
||||
let retry = with_cancel_hook ~action st (fun () ->
|
||||
@ -400,8 +396,9 @@ let run ~extra_effects st main arg =
|
||||
Fiber_context.destroy fiber;
|
||||
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
|
||||
| Get -> Some (fun k -> continue k st)
|
||||
| Enter fn -> Some (fun k ->
|
||||
match Fiber_context.get_error fiber with
|
||||
| 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;
|
||||
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
|
||||
}
|
||||
in
|
||||
@ -479,7 +460,7 @@ let run ~extra_effects st main arg =
|
||||
let `Exit_scheduler =
|
||||
let new_fiber = Fiber_context.make_root () in
|
||||
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 () ->
|
||||
fork ~new_fiber (fun () ->
|
||||
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)")
|
||||
| uring ->
|
||||
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;
|
||||
fallback (`Msg "Linux >= 5.11 is required for io_uring support")
|
||||
fallback (`Msg "Linux >= 5.15 is required for io_uring support")
|
||||
) 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
|
||||
let mem =
|
||||
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;
|
||||
let sleep_q = Zzz.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 ->
|
||||
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 }
|
||||
|
@ -130,7 +130,7 @@ let test_read_exact () =
|
||||
let ( / ) = Eio.Path.( / ) in
|
||||
let path = env#cwd / "test.data" 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 ->
|
||||
let fd = Eio_linux.Low_level.openat2 ~sw
|
||||
~access:`R
|
||||
@ -162,7 +162,7 @@ let test_statx () =
|
||||
Eio_linux.run ~queue_depth:4 @@ fun env ->
|
||||
let ( / ) = Eio.Path.( / ) 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;
|
||||
let buf = Uring.Statx.create () in
|
||||
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 open Alcotest in
|
||||
run "eio_linux" [
|
||||
"io", [
|
||||
test_case "copy" `Quick test_copy;
|
||||
test_case "direct_copy" `Quick test_direct_copy;
|
||||
test_case "poll_add" `Quick test_poll_add;
|
||||
test_case "poll_add_busy" `Quick test_poll_add_busy;
|
||||
test_case "iovec" `Quick test_iovec;
|
||||
test_case "no_sqe" `Quick test_no_sqe;
|
||||
test_case "read_exact" `Quick test_read_exact;
|
||||
test_case "expose_backend" `Quick test_expose_backend;
|
||||
test_case "statx" `Quick test_statx;
|
||||
test_case "copy" `Quick test_copy;
|
||||
test_case "direct_copy" `Quick test_direct_copy;
|
||||
test_case "poll_add" `Quick test_poll_add;
|
||||
test_case "poll_add_busy" `Quick test_poll_add_busy;
|
||||
test_case "iovec" `Quick test_iovec;
|
||||
test_case "no_sqe" `Quick test_no_sqe;
|
||||
test_case "read_exact" `Quick test_read_exact;
|
||||
test_case "expose_backend" `Quick test_expose_backend;
|
||||
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;
|
||||
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 ->
|
||||
let fd = Fd.of_unix ~sw ~blocking:false ~close_unix unix_fd in
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
CAMLparam1(v_actions);
|
||||
pid_t child_pid;
|
||||
|
@ -94,6 +94,9 @@ end = struct
|
||||
| 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
|
||||
|
||||
let symlink ~link_to t path =
|
||||
Err.run (Low_level.symlink ~link_to t.fd) path
|
||||
|
||||
let open_dir t ~sw path =
|
||||
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
|
||||
|
@ -8,7 +8,7 @@ let optional_flags = [
|
||||
|
||||
let () =
|
||||
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 extra_flags, missing_defs =
|
||||
C.C_define.import c ~c_flags ~includes
|
||||
|
@ -263,7 +263,8 @@ module Resolve = struct
|
||||
| new_base ->
|
||||
state.dir_stack <- Tmp (new_base, state.dir_stack);
|
||||
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
|
||||
| target ->
|
||||
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
|
||||
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 =
|
||||
in_worker_thread "read_link" @@ fun () ->
|
||||
Resolve.with_parent "read_link" dirfd path @@ fun dirfd path ->
|
||||
@ -549,6 +558,7 @@ module Process = struct
|
||||
let t =
|
||||
let pid =
|
||||
Fd.use_exn "errors-w" errors_w @@ fun errors_w ->
|
||||
Eio.Private.Trace.with_span "spawn" @@ fun () ->
|
||||
eio_spawn errors_w c_actions
|
||||
in
|
||||
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 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 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_unlinkat(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_fstatat(value, value, 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 new_fiber = Fiber_context.make_root () in
|
||||
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 () ->
|
||||
fork ~new_fiber (fun () ->
|
||||
Eio_unix.Private.Thread_pool.run t.thread_pool @@ fun () ->
|
||||
|
@ -3,8 +3,8 @@
|
||||
(enabled_if (= %{os_type} "Unix"))
|
||||
(deps (package eio_posix)))
|
||||
|
||||
(test
|
||||
(name open_beneath)
|
||||
(tests
|
||||
(names open_beneath test_await)
|
||||
(package eio_posix)
|
||||
(build_if (= %{os_type} "Unix"))
|
||||
(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, _, _) -> ());
|
||||
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 ->
|
||||
let fd = Fd.of_unix ~sw ~blocking:false ~close_unix unix_fd in
|
||||
Unix.set_nonblock unix_fd;
|
||||
|
@ -6,7 +6,7 @@
|
||||
(foreign_stubs
|
||||
(language c)
|
||||
(include_dirs ../lib_eio/unix/include)
|
||||
(names eio_windows_stubs eio_windows_cstruct_stubs))
|
||||
(names eio_windows_stubs))
|
||||
(c_library_flags :standard -lbcrypt -lntdll)
|
||||
(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)) {
|
||||
caml_win32_maperr(RtlNtStatusToDosError(r));
|
||||
uerror("openat", Nothing);
|
||||
uerror("openat", v_pathname);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
| EXDEV | EACCES | EPERM -> Eio.Fs.err (Permission_denied 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
|
||||
|
||||
let run fn x =
|
||||
|
@ -82,7 +82,7 @@ end = struct
|
||||
let dir = resolve t dir in
|
||||
Switch.run @@ fun sw ->
|
||||
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
|
||||
)
|
||||
) else fn None path
|
||||
@ -172,6 +172,10 @@ end = struct
|
||||
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
|
||||
|
||||
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 open_dir t ~sw path =
|
||||
|
@ -39,17 +39,25 @@ let rec do_nonblocking ty fn fd =
|
||||
do_nonblocking ty fn fd
|
||||
|
||||
let read fd buf start len =
|
||||
await_readable fd;
|
||||
Fd.use_exn "read" fd @@ fun 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 ->
|
||||
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 =
|
||||
await_writable fd;
|
||||
Fd.use_exn "write" fd @@ fun 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 =
|
||||
Sched.enter @@ fun t k ->
|
||||
Sched.await_timeout t k time
|
||||
@ -148,8 +156,11 @@ let readv fd bufs =
|
||||
do_nonblocking Read (fun fd -> eio_readv fd bufs) fd
|
||||
|
||||
let writev fd bufs =
|
||||
Fd.use_exn "writev" fd @@ fun fd ->
|
||||
do_nonblocking Write (fun fd -> Unix_cstruct.writev fd bufs) fd
|
||||
let rec loop buf = if Cstruct.length buf > 0 then begin
|
||||
let n = write_cstruct fd buf in
|
||||
loop @@ Cstruct.shift buf n
|
||||
end in
|
||||
List.iter loop bufs
|
||||
|
||||
let preadv ~file_offset fd bufs =
|
||||
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 () ->
|
||||
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 =
|
||||
Fd.use_exn "lseek" fd @@ fun fd ->
|
||||
let cmd =
|
||||
|
@ -22,6 +22,7 @@ val sleep_until : Mtime.t -> unit
|
||||
val read : fd -> bytes -> int -> int -> int
|
||||
val read_cstruct : fd -> Cstruct.t -> 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 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 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 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 =
|
||||
Eio.Resource.T (fd, datagram_handler)
|
||||
|
||||
(* https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml *)
|
||||
let getaddrinfo ~service node =
|
||||
let to_eio_sockaddr_t {Unix.ai_family; ai_addr; ai_socktype; ai_protocol; _ } =
|
||||
match ai_family, ai_socktype, ai_addr with
|
||||
| (Unix.PF_INET | PF_INET6),
|
||||
(Unix.SOCK_STREAM | SOCK_DGRAM),
|
||||
Unix.ADDR_INET (inet_addr,port) -> (
|
||||
match ai_protocol with
|
||||
| 6 -> Some (`Tcp (Eio_unix.Net.Ipaddr.of_unix inet_addr, port))
|
||||
| 17 -> Some (`Udp (Eio_unix.Net.Ipaddr.of_unix inet_addr, port))
|
||||
| _ -> None)
|
||||
| _ -> None
|
||||
(* OCaml's [Unix.getaddrinfo] on Windows doesn't set [ai_protocol] to
|
||||
anything useful, so you can't tell which addresses are TCP and which are
|
||||
UDP. So, do two separate queries. *)
|
||||
let get ty k =
|
||||
Unix.getaddrinfo node service [AI_SOCKTYPE ty]
|
||||
|> List.filter_map (function
|
||||
| {Unix.ai_addr = ADDR_INET (host, port); _} ->
|
||||
Some (k (Eio_unix.Net.Ipaddr.of_unix host, port))
|
||||
| _ -> None
|
||||
)
|
||||
in
|
||||
Err.run (Eio_unix.run_in_systhread ~label:"getaddrinfo") @@ fun () ->
|
||||
let rec aux () =
|
||||
try
|
||||
Unix.getaddrinfo node service []
|
||||
|> List.filter_map to_eio_sockaddr_t
|
||||
get SOCK_STREAM (fun x -> `Tcp x) @
|
||||
get SOCK_DGRAM (fun x -> `Udp x)
|
||||
with Unix.Unix_error (EINTR, _, _) -> aux ()
|
||||
in
|
||||
aux ()
|
||||
|
@ -271,6 +271,8 @@ let await_readable t (k : unit Suspended.t) fd =
|
||||
if was_empty then update t waiters fd;
|
||||
Fiber_context.set_cancel_fn k.fiber (fun ex ->
|
||||
Lwt_dllist.remove node;
|
||||
if Lwt_dllist.is_empty waiters.read then
|
||||
update t waiters fd;
|
||||
t.active_ops <- t.active_ops - 1;
|
||||
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;
|
||||
Fiber_context.set_cancel_fn k.fiber (fun ex ->
|
||||
Lwt_dllist.remove node;
|
||||
if Lwt_dllist.is_empty waiters.write then
|
||||
update t waiters fd;
|
||||
t.active_ops <- t.active_ops - 1;
|
||||
enqueue_failed_thread t k ex
|
||||
);
|
||||
@ -370,7 +374,7 @@ let run ~extra_effects t main x =
|
||||
let `Exit_scheduler =
|
||||
let new_fiber = Fiber_context.make_root () in
|
||||
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 () ->
|
||||
fork ~new_fiber (fun () ->
|
||||
Eio_unix.Private.Thread_pool.run t.thread_pool @@ fun () ->
|
||||
|
@ -1,3 +1,5 @@
|
||||
open Eio.Std
|
||||
|
||||
module Timeout = struct
|
||||
let test clock () =
|
||||
let t0 = Unix.gettimeofday () in
|
||||
@ -48,6 +50,35 @@ module Dla = struct
|
||||
]
|
||||
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 () =
|
||||
Eio_windows.run @@ fun env ->
|
||||
@ -56,5 +87,6 @@ let () =
|
||||
"fs", Test_fs.tests env;
|
||||
"timeout", Timeout.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;
|
||||
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
|
||||
try_mkdir (cwd / "sandbox");
|
||||
Unix.symlink ~to_dir:true ".." "sandbox\\to-root";
|
||||
@ -277,4 +280,5 @@ let tests env = [
|
||||
"unlink", `Quick, test_unlink env;
|
||||
"failing-unlink", `Quick, try_failing_unlink 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
|
||||
else
|
||||
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
|
||||
|
||||
Creating a file and reading it back:
|
||||
|
||||
```ocaml
|
||||
# run ~clear:["test-file"] @@ fun env ->
|
||||
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):
|
||||
|
||||
```ocaml
|
||||
# Printf.printf "Perm = %o\n" ((Unix.stat "test-file").st_perm);;
|
||||
Perm = 644
|
||||
@ -113,6 +120,7 @@ Perm = 644
|
||||
# Sandboxing
|
||||
|
||||
Trying to use cwd to access a file outside of that subtree fails:
|
||||
|
||||
```ocaml
|
||||
# run @@ fun env ->
|
||||
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:
|
||||
|
||||
```ocaml
|
||||
# run @@ fun env ->
|
||||
let cwd = Eio.Stdenv.cwd env in
|
||||
@ -135,6 +144,7 @@ Exception: Eio.Io Fs Permission_denied _,
|
||||
# Creation modes
|
||||
|
||||
Exclusive create fails if already exists:
|
||||
|
||||
```ocaml
|
||||
# run @@ fun env ->
|
||||
let cwd = Eio.Stdenv.cwd env in
|
||||
@ -146,6 +156,7 @@ Exception: Eio.Io Fs Already_exists _,
|
||||
```
|
||||
|
||||
If-missing create succeeds if already exists:
|
||||
|
||||
```ocaml
|
||||
# run @@ fun env ->
|
||||
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:
|
||||
|
||||
```ocaml
|
||||
# run @@ fun env ->
|
||||
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:
|
||||
|
||||
```ocaml
|
||||
# run @@ fun env ->
|
||||
let cwd = Eio.Stdenv.cwd env in
|
||||
@ -183,6 +196,7 @@ Exception: Eio.Io Fs Not_found _,
|
||||
```
|
||||
|
||||
Appending to an existing file:
|
||||
|
||||
```ocaml
|
||||
# run @@ fun env ->
|
||||
let cwd = Eio.Stdenv.cwd env in
|
||||
@ -215,12 +229,13 @@ Appending to an existing file:
|
||||
```
|
||||
|
||||
Creating directories with nesting, symlinks, etc:
|
||||
|
||||
```ocaml
|
||||
# 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
|
||||
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 / "to-subdir/nested");
|
||||
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
|
||||
try_write_file ~create:(`Exclusive 0o600) file1 "data1";
|
||||
try_write_file ~create:(`Exclusive 0o400) file2 "data2";
|
||||
Unix.symlink "dir1/file1" "link1";
|
||||
Unix.symlink "../file2" "dir1/link2";
|
||||
Unix.symlink "dir1" "linkdir";
|
||||
Unix.symlink "/" "linkroot";
|
||||
Path.symlink ~link_to:"dir1/file1" (cwd / "link1");
|
||||
Path.symlink ~link_to:"../file2" (cwd / "dir1/link2");
|
||||
Path.symlink ~link_to:"dir1" (cwd / "linkdir");
|
||||
Path.symlink ~link_to:"/" (cwd / "linkroot");
|
||||
try_read_file file1;
|
||||
try_read_file (cwd / "link1");
|
||||
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
|
||||
|
||||
Create a sandbox, write a file with it, then read it from outside:
|
||||
|
||||
```ocaml
|
||||
# run ~clear:["sandbox"] @@ fun env ->
|
||||
Switch.run @@ fun sw ->
|
||||
@ -540,10 +556,10 @@ Create a sandbox, write a file with it, then read it from outside:
|
||||
reject (cwd / "/");
|
||||
test (cwd / "foo/bar/..");
|
||||
test (fs / "foo/bar");
|
||||
Unix.symlink ".." "foo/up";
|
||||
Path.symlink ~link_to:".." (cwd / "foo/up");
|
||||
test (cwd / "foo/up/foo/bar");
|
||||
reject (cwd / "foo/up/../bar");
|
||||
Unix.symlink "/" "foo/root";
|
||||
Path.symlink ~link_to:"/" (cwd / "foo/root");
|
||||
reject (cwd / "foo/root/..");
|
||||
reject (cwd / "missing");
|
||||
+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.
|
||||
Using `cwd` we can't access the parent, but using `fs` we can:
|
||||
|
||||
```ocaml
|
||||
# run ~clear:["fs-test"; "outside-cwd"] @@ fun env ->
|
||||
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 / "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");
|
||||
+mkdir <cwd:readdir> -> ok
|
||||
+mkdir <readdir:test-1> -> ok
|
||||
@ -650,6 +667,29 @@ Exception: Eio.Io Fs Permission_denied _,
|
||||
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
|
||||
|
||||
```ocaml
|
||||
@ -781,15 +821,15 @@ Unconfined:
|
||||
let cwd = Eio.Stdenv.cwd env in
|
||||
Switch.run @@ fun sw ->
|
||||
try_mkdir (cwd / "stat_subdir2");
|
||||
Unix.symlink "stat_subdir2" "symlink";
|
||||
Unix.symlink "missing" "broken-symlink";
|
||||
Path.symlink ~link_to:"stat_subdir2" (cwd / "symlink");
|
||||
Path.symlink ~link_to:"missing" (cwd / "broken-symlink");
|
||||
try_stat (cwd / "stat_subdir2");
|
||||
try_stat (cwd / "symlink");
|
||||
try_stat (cwd / "broken-symlink");
|
||||
try_stat cwd;
|
||||
try_stat (cwd / "..");
|
||||
try_stat (cwd / "stat_subdir2/..");
|
||||
Unix.symlink ".." "parent-symlink";
|
||||
Path.symlink ~link_to:".." (cwd / "parent-symlink");
|
||||
try_stat (cwd / "parent-symlink");
|
||||
try_stat (cwd / "missing1" / "missing2");
|
||||
+mkdir <cwd:stat_subdir2> -> ok
|
||||
@ -811,7 +851,7 @@ Unconfined:
|
||||
let fs = Eio.Stdenv.fs env in
|
||||
let cwd = Eio.Stdenv.cwd env in
|
||||
Switch.run @@ fun sw ->
|
||||
Unix.symlink "file" "symlink";
|
||||
Path.symlink ~link_to:"file" (cwd / "symlink");
|
||||
try_read_link (cwd / "symlink");
|
||||
try_read_link (fs / "symlink");
|
||||
try_write_file (cwd / "file") "data" ~create:(`Exclusive 0o600);
|
||||
@ -945,3 +985,29 @@ Exception: Failure "Simulated error".
|
||||
+seek from end: 9
|
||||
- : 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 = ()
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```ocaml
|
||||
@ -597,7 +620,9 @@ Exception: Eio.Io Fs Not_found _,
|
||||
```ocaml
|
||||
# Eio_main.run @@ fun env ->
|
||||
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")
|
||||
```
|
||||
|
||||
|
@ -77,6 +77,37 @@ Two uses with a capacity of 2; they run in parallel:
|
||||
- : 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
|
||||
|
||||
```ocaml
|
||||
|
@ -145,24 +145,24 @@ If a command fails, we get shown the arguments (quoted if necessary):
|
||||
|
||||
```ocaml
|
||||
# run @@ fun mgr env ->
|
||||
Process.run mgr ["bash"; "-c"; "exit 3"; ""; "foo"; "\"bar\""];;
|
||||
Process.run mgr ["sh"; "-c"; "exit 3"; ""; "foo"; "\"bar\""];;
|
||||
Exception:
|
||||
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):
|
||||
|
||||
```ocaml
|
||||
# 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 = ()
|
||||
|
||||
# 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:
|
||||
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):
|
||||
|
@ -231,3 +231,21 @@ Cancellation:
|
||||
+Cancel sleeper
|
||||
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