├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── docs ├── benchmark.md ├── images │ ├── benchmark-1.png │ ├── benchmark-2.png │ └── benchmark.png └── logs │ ├── b1 │ ├── Photon-fiber.log │ ├── Photon-threaded.log │ ├── epoll-dmd.log │ ├── go-fasthttp.log │ └── tokio-minihttp.log │ └── b2 │ ├── Photon-empty.log │ └── epoll-dmd.log ├── dub.json ├── examples ├── configuration │ ├── .gitignore │ ├── application.conf │ ├── dub.json │ ├── source │ │ ├── app.d │ │ └── settings.d │ ├── test.config │ └── test2.config ├── logger │ ├── .gitignore │ ├── dub.json │ └── source │ │ └── app.d ├── performance │ ├── .gitignore │ ├── .vscode │ │ └── launch.json │ ├── dub.json │ └── source │ │ └── app.d ├── tcp │ ├── .vscode │ │ └── launch.json │ ├── build.bat │ ├── build.sh │ ├── dub.json │ └── source │ │ ├── client.d │ │ └── server.d ├── timer │ ├── .gitignore │ ├── .vscode │ │ └── launch.json │ ├── dub.json │ └── source │ │ └── app.d ├── udp │ ├── .gitignore │ ├── .vscode │ │ └── launch.json │ ├── build.bat │ ├── build.sh │ ├── dub.json │ └── source │ │ ├── client.d │ │ └── server.d └── worker │ ├── dub.json │ └── source │ └── app.d ├── hunt.code-workspace └── source └── hunt ├── Exceptions.d ├── Functions.d ├── event ├── EventLoop.d ├── EventLoopGroup.d ├── package.d ├── selector │ ├── Epoll.d │ ├── IOCP.d │ ├── Kqueue.d │ ├── Selector.d │ └── package.d └── timer │ ├── Common.d │ ├── Epoll.d │ ├── IOCP.d │ ├── Kqueue.d │ └── package.d ├── io ├── Buffer.d ├── BufferUtils.d ├── ByteBuffer.d ├── HeapByteBuffer.d ├── IoError.d ├── SimpleQueue.d ├── TcpListener.d ├── TcpStream.d ├── TcpStreamOptions.d ├── UdpSocket.d ├── channel │ ├── AbstractChannel.d │ ├── AbstractSocketChannel.d │ ├── ChannelTask.d │ ├── Common.d │ ├── iocp │ │ ├── AbstractDatagramSocket.d │ │ ├── AbstractListener.d │ │ ├── AbstractStream.d │ │ ├── Common.d │ │ └── package.d │ ├── package.d │ └── posix │ │ ├── AbstractDatagramSocket.d │ │ ├── AbstractListener.d │ │ ├── AbstractStream.d │ │ ├── EpollEventChannel.d │ │ ├── KqueueEventChannel.d │ │ └── package.d └── package.d ├── logging ├── ConsoleLogger.d ├── Helper.d ├── Logger.d └── package.d ├── serialization ├── BinaryDeserializer.d ├── BinarySerialization.d ├── BinarySerializer.d ├── Common.d ├── JsonSerializer.d ├── Specify.d └── package.d ├── system ├── Error.d ├── Memory.d ├── WindowsHelper.d ├── package.d └── syscall │ ├── os │ ├── FreeBSD.d │ ├── Linux.d │ └── OSX.d │ └── package.d └── util ├── Appendable.d ├── ByteOrder.d ├── Closeable.d ├── CompilerHelper.d ├── Configuration.d ├── ConverterUtils.d ├── DateTime.d ├── Lifecycle.d ├── ResoureManager.d ├── Runnable.d ├── Serialize.d ├── StringBuilder.d ├── TaskPool.d ├── ThreadHelper.d ├── Timer.d ├── Traits.d ├── queue ├── Queue.d ├── SimpleQueue.d └── package.d └── worker ├── Task.d ├── Worker.d ├── WorkerThread.d └── package.d /.gitignore: -------------------------------------------------------------------------------- 1 | # Visual Studio Code 2 | #.vscode/ 3 | .suo 4 | 5 | # Compiled Object files 6 | *.o 7 | *.obj 8 | 9 | # Compiled Dynamic libraries 10 | *.so 11 | *.dylib 12 | *.dll 13 | 14 | # Compiled Static libraries 15 | *.a 16 | *.lib 17 | 18 | # Executables 19 | *.exe 20 | 21 | # DUB 22 | .dub 23 | dub.*.json 24 | docs.json 25 | __dummy.html 26 | 27 | 28 | # Code coverage 29 | *.lst 30 | 31 | # Examples 32 | examples/*/log.txt 33 | examples/configuration/configuration 34 | examples/collecton/container-demo 35 | examples/udp/udp-client 36 | examples/udp/udp-server 37 | examples/performance/performance 38 | examples/tcp/tcp-client 39 | examples/tcp/tcp-server 40 | examples/timer/timer 41 | examples/test/unittest 42 | examples/worker/worker 43 | 44 | # others 45 | *-test-* 46 | .DS_Store 47 | *.zip 48 | *-test-* 49 | mixin_output.d 50 | 51 | # meson build directories 52 | build 53 | _build 54 | 55 | # idea 56 | .idea 57 | *.iml 58 | core 59 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: d 2 | dist: bionic 3 | sudo: true 4 | os: 5 | - linux 6 | - osx 7 | d: 8 | - dmd 9 | - ldc 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/huntlabs/hunt.svg?branch=master)](https://travis-ci.org/huntlabs/hunt) 2 | 3 | # Hunt library 4 | A refined core library for D programming language. 5 | 6 | ## Modules 7 | * hunt.event 8 | * hunt.io 9 | * hunt.logging 10 | * hunt.util 11 | 12 | ## Platforms 13 | * FreeBSD 14 | * Windows 15 | * macOS 16 | * Linux 17 | * Android 18 | 19 | ## Libraries 20 | * [hunt-net](https://github.com/huntlabs/hunt-net) – An asynchronous event-driven network library written in D. 21 | 22 | ## Frameworks 23 | * [hunt-framework](https://github.com/huntlabs/hunt-framework) – Hunt is a high-level D Programming Language Web framework that encourages rapid development and clean, pragmatic design. It lets you build high-performance Web applications quickly and easily. 24 | 25 | ## Requirements 26 | D Compiler 2.088+. 27 | 28 | ## Avaliable versions 29 | | Identifier | Description | 30 | |--------|--------| 31 | | HUNT_DEBUG | Used to log some debug messages | 32 | 33 | ## Benchmarks 34 | ![Benchmark](docs/images/benchmark.png) 35 | 36 | For details, see [here](docs/benchmark.md). 37 | 38 | ## Thanks 39 | * @Cogitri 40 | * @deviator 41 | * @jasonwhite 42 | * @Kripth 43 | * @n8sh 44 | * @shove70 45 | 46 | ## TODO 47 | - [ ] Better performance 48 | - [ ] Better APIs 49 | - [ ] Improvement for Worker with IOCP 50 | -------------------------------------------------------------------------------- /docs/benchmark.md: -------------------------------------------------------------------------------- 1 | ## Hunt Benchmark 2 | 3 | ### Environment 4 | - **Server** (physical server / 12 cores): SMP Debian 3.16.7 x86_64 GNU/Linux 5 | 6 | 7 | ### Compilers 8 | - **DMD:** 2.083 9 | - **GO:** v1.10.1 10 | - **Rust:** v1.25.0 11 | 12 | ### Tools 13 | - **wrk:** v4.1.0 14 | - **bash script:** [../examples/Benchmark/bench.sh](../examples/Benchmark/bench.sh) 15 | - **command line:** `$ ./bench.sh Hunt plaintext 127.0.0.1 8080` 16 | 17 | ### Benchmarking (plaintext) 18 | 19 | #### case one 20 | The Date field in http header is set 21 | ![Benchmark](images/benchmark-1.png) 22 | 23 | [The logs](logs/b1/) 24 | 25 | #### case two 26 | Just repson with static string. 27 | ![Benchmark](images/benchmark-2.png) 28 | 29 | [The logs](logs/b2/) 30 | 31 | ### See also 32 | 33 | [1] Hunt: https://github.com/huntlabs/hunt/ 34 | 35 | [2] Hunt-Minihttp: https://github.com/huntlabs/hunt-minihttp 36 | 37 | [3] Photon: https://github.com/DmitryOlshansky/photon/ 38 | 39 | [4] Tokio MiniHTTP: https://github.com/tokio-rs/tokio-minihttp/ 40 | 41 | [5] fasthttp: https://github.com/valyala/fasthttp/ 42 | 43 | [6] FrameworkBenchmarks: https://github.com/TechEmpower/FrameworkBenchmarks/ 44 | 45 | [7] wrk: https://github.com/wg/wrk/ 46 | 47 | -------------------------------------------------------------------------------- /docs/images/benchmark-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huntlabs/hunt/ffca2afb1cdafd90fbe0653e01429b2f5488a122/docs/images/benchmark-1.png -------------------------------------------------------------------------------- /docs/images/benchmark-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huntlabs/hunt/ffca2afb1cdafd90fbe0653e01429b2f5488a122/docs/images/benchmark-2.png -------------------------------------------------------------------------------- /docs/images/benchmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huntlabs/hunt/ffca2afb1cdafd90fbe0653e01429b2f5488a122/docs/images/benchmark.png -------------------------------------------------------------------------------- /docs/logs/b1/Photon-fiber.log: -------------------------------------------------------------------------------- 1 | ops@pressure:~/benchmark$ ./bench.sh photon-fiber plaintext 127.0.0.1 8080 2 | Usage: ./bench.sh Name plaintext 127.0.0.1 8080 3 | 4 | --------------------------------------------------------- 5 | Running Primer photon-fiber 6 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 5 -c 8 --timeout 8 -t 8 http://127.0.0.1:8080/plaintext 7 | --------------------------------------------------------- 8 | 9 | Running 5s test @ http://127.0.0.1:8080/plaintext 10 | 8 threads and 8 connections 11 | Thread Stats Avg Stdev Max +/- Stdev 12 | Latency 68.80us 400.89us 16.03ms 99.56% 13 | Req/Sec 20.04k 0.97k 22.48k 86.98% 14 | Latency Distribution 15 | 50% 46.00us 16 | 75% 51.00us 17 | 90% 59.00us 18 | 99% 87.00us 19 | 811388 requests in 5.10s, 118.39MB read 20 | Requests/sec: 159108.10 21 | Transfer/sec: 23.22MB 22 | 23 | --------------------------------------------------------- 24 | Running Warmup photon-fiber 25 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 26 | --------------------------------------------------------- 27 | 28 | Running 15s test @ http://127.0.0.1:8080/plaintext 29 | 12 threads and 512 connections 30 | Thread Stats Avg Stdev Max +/- Stdev 31 | Latency 1.87ms 1.75ms 43.44ms 79.70% 32 | Req/Sec 24.99k 3.10k 38.06k 67.72% 33 | Latency Distribution 34 | 50% 1.71ms 35 | 75% 2.47ms 36 | 90% 3.42ms 37 | 99% 8.23ms 38 | 4479129 requests in 15.02s, 653.56MB read 39 | Requests/sec: 298118.12 40 | Transfer/sec: 43.50MB 41 | 42 | --------------------------------------------------------- 43 | Concurrency: 512 for photon-fiber 44 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 45 | --------------------------------------------------------- 46 | 47 | Running 15s test @ http://127.0.0.1:8080/plaintext 48 | 12 threads and 512 connections 49 | Thread Stats Avg Stdev Max +/- Stdev 50 | Latency 1.83ms 1.62ms 36.84ms 78.12% 51 | Req/Sec 24.87k 3.01k 34.44k 66.33% 52 | Latency Distribution 53 | 50% 1.69ms 54 | 75% 2.44ms 55 | 90% 3.35ms 56 | 99% 7.78ms 57 | 4457567 requests in 15.03s, 650.41MB read 58 | Requests/sec: 296577.90 59 | Transfer/sec: 43.27MB 60 | STARTTIME 1543841070 61 | ENDTIME 1543841085 62 | 63 | --------------------------------------------------------- 64 | Concurrency: 512 for photon-fiber 65 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 66 | --------------------------------------------------------- 67 | 68 | Running 15s test @ http://127.0.0.1:8080/plaintext 69 | 12 threads and 512 connections 70 | Thread Stats Avg Stdev Max +/- Stdev 71 | Latency 1.86ms 1.61ms 41.08ms 75.27% 72 | Req/Sec 24.50k 2.92k 32.86k 68.72% 73 | Latency Distribution 74 | 50% 1.75ms 75 | 75% 2.54ms 76 | 90% 3.39ms 77 | 99% 7.36ms 78 | 4390899 requests in 15.02s, 640.69MB read 79 | Requests/sec: 292290.02 80 | Transfer/sec: 42.65MB 81 | STARTTIME 1543841087 82 | ENDTIME 1543841102 83 | 84 | --------------------------------------------------------- 85 | Concurrency: 512 for photon-fiber 86 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 87 | --------------------------------------------------------- 88 | 89 | Running 15s test @ http://127.0.0.1:8080/plaintext 90 | 12 threads and 512 connections 91 | Thread Stats Avg Stdev Max +/- Stdev 92 | Latency 1.84ms 1.63ms 30.41ms 77.74% 93 | Req/Sec 24.82k 2.88k 35.00k 68.61% 94 | Latency Distribution 95 | 50% 1.73ms 96 | 75% 2.45ms 97 | 90% 3.33ms 98 | 99% 7.90ms 99 | 4447128 requests in 15.02s, 648.89MB read 100 | Requests/sec: 295997.64 101 | Transfer/sec: 43.19MB 102 | STARTTIME 1543841104 103 | ENDTIME 1543841119 104 | 105 | --------------------------------------------------------- 106 | Concurrency: 512 for photon-fiber 107 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 108 | --------------------------------------------------------- 109 | 110 | Running 15s test @ http://127.0.0.1:8080/plaintext 111 | 12 threads and 512 connections 112 | Thread Stats Avg Stdev Max +/- Stdev 113 | Latency 1.87ms 1.72ms 47.40ms 78.50% 114 | Req/Sec 24.95k 3.10k 33.40k 67.39% 115 | Latency Distribution 116 | 50% 1.72ms 117 | 75% 2.52ms 118 | 90% 3.44ms 119 | 99% 7.97ms 120 | 4470984 requests in 15.02s, 652.37MB read 121 | Requests/sec: 297621.13 122 | Transfer/sec: 43.43MB 123 | STARTTIME 1543841121 124 | ENDTIME 1543841136 125 | 126 | --------------------------------------------------------- 127 | Concurrency: 512 for photon-fiber 128 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 129 | --------------------------------------------------------- 130 | 131 | Running 15s test @ http://127.0.0.1:8080/plaintext 132 | 12 threads and 512 connections 133 | Thread Stats Avg Stdev Max +/- Stdev 134 | Latency 1.84ms 1.61ms 45.62ms 77.34% 135 | Req/Sec 24.69k 2.94k 37.95k 67.72% 136 | Latency Distribution 137 | 50% 1.74ms 138 | 75% 2.48ms 139 | 90% 3.29ms 140 | 99% 7.48ms 141 | 4426680 requests in 15.03s, 645.91MB read 142 | Requests/sec: 294611.95 143 | Transfer/sec: 42.99MB 144 | STARTTIME 1543841138 145 | ENDTIME 1543841153 146 | 147 | 148 | -------------------------------------------------------------------------------- /docs/logs/b1/Photon-threaded.log: -------------------------------------------------------------------------------- 1 | ops@pressure:~/benchmark$ ./bench.sh photon-threaded plaintext 127.0.0.1 8080 2 | Usage: ./bench.sh Name plaintext 127.0.0.1 8080 3 | 4 | --------------------------------------------------------- 5 | Running Primer photon-threaded 6 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 5 -c 8 --timeout 8 -t 8 http://127.0.0.1:8080/plaintext 7 | --------------------------------------------------------- 8 | 9 | Running 5s test @ http://127.0.0.1:8080/plaintext 10 | 8 threads and 8 connections 11 | Thread Stats Avg Stdev Max +/- Stdev 12 | Latency 39.06us 182.08us 12.10ms 99.89% 13 | Req/Sec 27.77k 2.62k 31.20k 53.92% 14 | Latency Distribution 15 | 50% 34.00us 16 | 75% 36.00us 17 | 90% 38.00us 18 | 99% 58.00us 19 | 1127257 requests in 5.10s, 120.40MB read 20 | Requests/sec: 221043.83 21 | Transfer/sec: 23.61MB 22 | 23 | --------------------------------------------------------- 24 | Running Warmup photon-threaded 25 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 26 | --------------------------------------------------------- 27 | 28 | Running 15s test @ http://127.0.0.1:8080/plaintext 29 | 12 threads and 512 connections 30 | Thread Stats Avg Stdev Max +/- Stdev 31 | Latency 0.95ms 2.93ms 111.04ms 99.51% 32 | Req/Sec 27.47k 6.45k 86.47k 84.56% 33 | Latency Distribution 34 | 50% 774.00us 35 | 75% 0.94ms 36 | 90% 1.14ms 37 | 99% 2.23ms 38 | 4926203 requests in 15.10s, 526.18MB read 39 | Requests/sec: 326255.66 40 | Transfer/sec: 34.85MB 41 | 42 | --------------------------------------------------------- 43 | Concurrency: 512 for photon-threaded 44 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 45 | --------------------------------------------------------- 46 | 47 | Running 15s test @ http://127.0.0.1:8080/plaintext 48 | 12 threads and 512 connections 49 | Thread Stats Avg Stdev Max +/- Stdev 50 | Latency 0.88ms 1.77ms 98.00ms 99.57% 51 | Req/Sec 27.19k 4.40k 74.60k 84.65% 52 | Latency Distribution 53 | 50% 792.00us 54 | 75% 0.94ms 55 | 90% 1.06ms 56 | 99% 1.52ms 57 | 4883860 requests in 15.10s, 521.65MB read 58 | Requests/sec: 323455.77 59 | Transfer/sec: 34.55MB 60 | STARTTIME 1543841522 61 | ENDTIME 1543841537 62 | 63 | --------------------------------------------------------- 64 | Concurrency: 512 for photon-threaded 65 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 66 | --------------------------------------------------------- 67 | 68 | Running 15s test @ http://127.0.0.1:8080/plaintext 69 | 12 threads and 512 connections 70 | Thread Stats Avg Stdev Max +/- Stdev 71 | Latency 0.89ms 1.75ms 75.19ms 99.02% 72 | Req/Sec 27.68k 9.90k 82.72k 76.22% 73 | Latency Distribution 74 | 50% 752.00us 75 | 75% 0.98ms 76 | 90% 1.27ms 77 | 99% 2.62ms 78 | 4962744 requests in 15.04s, 530.08MB read 79 | Requests/sec: 329934.52 80 | Transfer/sec: 35.24MB 81 | STARTTIME 1543841539 82 | ENDTIME 1543841554 83 | 84 | --------------------------------------------------------- 85 | Concurrency: 512 for photon-threaded 86 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 87 | --------------------------------------------------------- 88 | 89 | Running 15s test @ http://127.0.0.1:8080/plaintext 90 | 12 threads and 512 connections 91 | Thread Stats Avg Stdev Max +/- Stdev 92 | Latency 841.20us 1.16ms 65.60ms 99.35% 93 | Req/Sec 27.41k 3.07k 82.63k 93.01% 94 | Latency Distribution 95 | 50% 783.00us 96 | 75% 0.98ms 97 | 90% 1.16ms 98 | 99% 1.60ms 99 | 4919916 requests in 15.10s, 525.50MB read 100 | Requests/sec: 325859.65 101 | Transfer/sec: 34.81MB 102 | STARTTIME 1543841556 103 | ENDTIME 1543841572 104 | 105 | --------------------------------------------------------- 106 | Concurrency: 512 for photon-threaded 107 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 108 | --------------------------------------------------------- 109 | 110 | Running 15s test @ http://127.0.0.1:8080/plaintext 111 | 12 threads and 512 connections 112 | Thread Stats Avg Stdev Max +/- Stdev 113 | Latency 839.20us 0.99ms 63.26ms 98.83% 114 | Req/Sec 27.55k 6.81k 83.76k 85.07% 115 | Latency Distribution 116 | 50% 789.00us 117 | 75% 0.94ms 118 | 90% 1.17ms 119 | 99% 1.93ms 120 | 4961318 requests in 15.10s, 529.93MB read 121 | Requests/sec: 328589.68 122 | Transfer/sec: 35.10MB 123 | STARTTIME 1543841574 124 | ENDTIME 1543841589 125 | 126 | --------------------------------------------------------- 127 | Concurrency: 512 for photon-threaded 128 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 129 | --------------------------------------------------------- 130 | 131 | Running 15s test @ http://127.0.0.1:8080/plaintext 132 | 12 threads and 512 connections 133 | Thread Stats Avg Stdev Max +/- Stdev 134 | Latency 0.92ms 2.11ms 77.58ms 99.13% 135 | Req/Sec 28.05k 12.97k 86.77k 80.11% 136 | Latency Distribution 137 | 50% 744.00us 138 | 75% 1.00ms 139 | 90% 1.35ms 140 | 99% 2.83ms 141 | 5027965 requests in 15.10s, 537.04MB read 142 | Requests/sec: 333048.92 143 | Transfer/sec: 35.57MB 144 | STARTTIME 1543841591 145 | ENDTIME 1543841606 146 | 147 | -------------------------------------------------------------------------------- /docs/logs/b1/epoll-dmd.log: -------------------------------------------------------------------------------- 1 | ops@pressure:~/benchmark$ ./bench.sh Hunt plaintext 127.0.0.1 8080 2 | Usage: ./bench.sh Name plaintext 127.0.0.1 8080 3 | 4 | --------------------------------------------------------- 5 | Running Primer Hunt 6 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 5 -c 8 --timeout 8 -t 8 http://127.0.0.1:8080/plaintext 7 | --------------------------------------------------------- 8 | 9 | Running 5s test @ http://127.0.0.1:8080/plaintext 10 | 8 threads and 8 connections 11 | Thread Stats Avg Stdev Max +/- Stdev 12 | Latency 71.36us 325.77us 14.66ms 97.33% 13 | Req/Sec 24.83k 2.43k 32.00k 79.66% 14 | Latency Distribution 15 | 50% 35.00us 16 | 75% 41.00us 17 | 90% 54.00us 18 | 99% 1.05ms 19 | 1007084 requests in 5.10s, 102.77MB read 20 | Requests/sec: 197464.28 21 | Transfer/sec: 20.15MB 22 | 23 | --------------------------------------------------------- 24 | Running Warmup Hunt 25 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 26 | --------------------------------------------------------- 27 | 28 | Running 15s test @ http://127.0.0.1:8080/plaintext 29 | 12 threads and 512 connections 30 | Thread Stats Avg Stdev Max +/- Stdev 31 | Latency 1.53ms 2.23ms 58.31ms 91.13% 32 | Req/Sec 38.31k 6.29k 84.46k 71.87% 33 | Latency Distribution 34 | 50% 797.00us 35 | 75% 1.71ms 36 | 90% 3.48ms 37 | 99% 11.03ms 38 | 6883381 requests in 15.09s, 702.40MB read 39 | Requests/sec: 456155.83 40 | Transfer/sec: 46.55MB 41 | 42 | --------------------------------------------------------- 43 | Concurrency: 512 for Hunt 44 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 45 | --------------------------------------------------------- 46 | 47 | Running 15s test @ http://127.0.0.1:8080/plaintext 48 | 12 threads and 512 connections 49 | Thread Stats Avg Stdev Max +/- Stdev 50 | Latency 1.53ms 2.32ms 57.07ms 91.63% 51 | Req/Sec 38.35k 6.28k 73.93k 70.83% 52 | Latency Distribution 53 | 50% 776.00us 54 | 75% 1.69ms 55 | 90% 3.43ms 56 | 99% 11.27ms 57 | 6897632 requests in 15.09s, 703.86MB read 58 | Requests/sec: 457000.50 59 | Transfer/sec: 46.63MB 60 | STARTTIME 1544077152 61 | ENDTIME 1544077167 62 | 63 | --------------------------------------------------------- 64 | Concurrency: 512 for Hunt 65 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 66 | --------------------------------------------------------- 67 | 68 | Running 15s test @ http://127.0.0.1:8080/plaintext 69 | 12 threads and 512 connections 70 | Thread Stats Avg Stdev Max +/- Stdev 71 | Latency 1.47ms 2.02ms 41.90ms 90.77% 72 | Req/Sec 38.40k 5.91k 67.82k 70.48% 73 | Latency Distribution 74 | 50% 802.00us 75 | 75% 1.66ms 76 | 90% 3.30ms 77 | 99% 10.33ms 78 | 6900478 requests in 15.09s, 704.15MB read 79 | Requests/sec: 457310.38 80 | Transfer/sec: 46.67MB 81 | STARTTIME 1544077169 82 | ENDTIME 1544077185 83 | 84 | --------------------------------------------------------- 85 | Concurrency: 512 for Hunt 86 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 87 | --------------------------------------------------------- 88 | 89 | Running 15s test @ http://127.0.0.1:8080/plaintext 90 | 12 threads and 512 connections 91 | Thread Stats Avg Stdev Max +/- Stdev 92 | Latency 1.45ms 1.96ms 54.28ms 90.56% 93 | Req/Sec 38.47k 6.11k 68.23k 71.50% 94 | Latency Distribution 95 | 50% 793.00us 96 | 75% 1.67ms 97 | 90% 3.29ms 98 | 99% 10.03ms 99 | 6915862 requests in 15.08s, 705.72MB read 100 | Requests/sec: 458599.19 101 | Transfer/sec: 46.80MB 102 | STARTTIME 1544077187 103 | ENDTIME 1544077202 104 | 105 | --------------------------------------------------------- 106 | Concurrency: 512 for Hunt 107 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 108 | --------------------------------------------------------- 109 | 110 | Running 15s test @ http://127.0.0.1:8080/plaintext 111 | 12 threads and 512 connections 112 | Thread Stats Avg Stdev Max +/- Stdev 113 | Latency 1.50ms 2.07ms 42.83ms 90.65% 114 | Req/Sec 38.32k 6.15k 73.93k 70.72% 115 | Latency Distribution 116 | 50% 797.00us 117 | 75% 1.70ms 118 | 90% 3.41ms 119 | 99% 10.55ms 120 | 6887676 requests in 15.09s, 702.84MB read 121 | Requests/sec: 456352.06 122 | Transfer/sec: 46.57MB 123 | STARTTIME 1544077204 124 | ENDTIME 1544077219 125 | 126 | --------------------------------------------------------- 127 | Concurrency: 512 for Hunt 128 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 129 | --------------------------------------------------------- 130 | 131 | Running 15s test @ http://127.0.0.1:8080/plaintext 132 | 12 threads and 512 connections 133 | Thread Stats Avg Stdev Max +/- Stdev 134 | Latency 1.48ms 2.01ms 39.34ms 90.58% 135 | Req/Sec 38.22k 5.85k 68.64k 68.56% 136 | Latency Distribution 137 | 50% 808.00us 138 | 75% 1.69ms 139 | 90% 3.36ms 140 | 99% 10.29ms 141 | 6868363 requests in 15.08s, 700.87MB read 142 | Requests/sec: 455574.21 143 | Transfer/sec: 46.49MB 144 | STARTTIME 1544077221 145 | ENDTIME 1544077236 146 | -------------------------------------------------------------------------------- /docs/logs/b1/tokio-minihttp.log: -------------------------------------------------------------------------------- 1 | ops@pressure:~/benchmark$ ./bench.sh tokio plaintext 127.0.0.1 8080 2 | Usage: ./bench.sh Name plaintext 127.0.0.1 8080 3 | 4 | --------------------------------------------------------- 5 | Running Primer tokio 6 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 5 -c 8 --timeout 8 -t 8 http://127.0.0.1:8080/plaintext 7 | --------------------------------------------------------- 8 | 9 | Running 5s test @ http://127.0.0.1:8080/plaintext 10 | 8 threads and 8 connections 11 | Thread Stats Avg Stdev Max +/- Stdev 12 | Latency 178.17us 210.90us 14.75ms 99.89% 13 | Req/Sec 5.73k 1.99k 10.01k 60.29% 14 | Latency Distribution 15 | 50% 156.00us 16 | 75% 211.00us 17 | 90% 272.00us 18 | 99% 299.00us 19 | 232742 requests in 5.10s, 28.63MB read 20 | Requests/sec: 45635.49 21 | Transfer/sec: 5.61MB 22 | 23 | --------------------------------------------------------- 24 | Running Warmup tokio 25 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 26 | --------------------------------------------------------- 27 | 28 | Running 15s test @ http://127.0.0.1:8080/plaintext 29 | 12 threads and 512 connections 30 | Thread Stats Avg Stdev Max +/- Stdev 31 | Latency 6.22ms 2.16ms 36.97ms 75.86% 32 | Req/Sec 6.84k 361.13 9.29k 73.98% 33 | Latency Distribution 34 | 50% 5.82ms 35 | 75% 7.26ms 36 | 90% 8.74ms 37 | 99% 13.56ms 38 | 1232006 requests in 15.10s, 151.57MB read 39 | Requests/sec: 81600.25 40 | Transfer/sec: 10.04MB 41 | 42 | --------------------------------------------------------- 43 | Concurrency: 512 for tokio 44 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 45 | --------------------------------------------------------- 46 | 47 | Running 15s test @ http://127.0.0.1:8080/plaintext 48 | 12 threads and 512 connections 49 | Thread Stats Avg Stdev Max +/- Stdev 50 | Latency 6.28ms 2.58ms 54.63ms 85.88% 51 | Req/Sec 6.86k 362.29 14.36k 85.79% 52 | Latency Distribution 53 | 50% 5.83ms 54 | 75% 7.07ms 55 | 90% 8.64ms 56 | 99% 15.80ms 57 | 1235518 requests in 15.10s, 152.00MB read 58 | Requests/sec: 81825.53 59 | Transfer/sec: 10.07MB 60 | STARTTIME 1543842074 61 | ENDTIME 1543842089 62 | 63 | --------------------------------------------------------- 64 | Concurrency: 512 for tokio 65 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 66 | --------------------------------------------------------- 67 | 68 | Running 15s test @ http://127.0.0.1:8080/plaintext 69 | 12 threads and 512 connections 70 | Thread Stats Avg Stdev Max +/- Stdev 71 | Latency 6.22ms 1.96ms 50.19ms 83.80% 72 | Req/Sec 6.84k 517.35 17.35k 88.09% 73 | Latency Distribution 74 | 50% 5.98ms 75 | 75% 6.92ms 76 | 90% 8.22ms 77 | 99% 13.39ms 78 | 1228790 requests in 15.10s, 151.17MB read 79 | Requests/sec: 81377.92 80 | Transfer/sec: 10.01MB 81 | STARTTIME 1543842091 82 | ENDTIME 1543842107 83 | 84 | --------------------------------------------------------- 85 | Concurrency: 512 for tokio 86 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 87 | --------------------------------------------------------- 88 | 89 | Running 15s test @ http://127.0.0.1:8080/plaintext 90 | 12 threads and 512 connections 91 | Thread Stats Avg Stdev Max +/- Stdev 92 | Latency 6.23ms 2.06ms 30.94ms 85.11% 93 | Req/Sec 6.83k 307.64 11.53k 76.13% 94 | Latency Distribution 95 | 50% 5.36ms 96 | 75% 6.71ms 97 | 90% 8.57ms 98 | 99% 14.54ms 99 | 1230778 requests in 15.10s, 151.42MB read 100 | Requests/sec: 81533.25 101 | Transfer/sec: 10.03MB 102 | STARTTIME 1543842109 103 | ENDTIME 1543842124 104 | 105 | --------------------------------------------------------- 106 | Concurrency: 512 for tokio 107 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 108 | --------------------------------------------------------- 109 | 110 | Running 15s test @ http://127.0.0.1:8080/plaintext 111 | 12 threads and 512 connections 112 | Thread Stats Avg Stdev Max +/- Stdev 113 | Latency 6.26ms 2.19ms 35.72ms 80.48% 114 | Req/Sec 6.80k 820.46 30.53k 97.39% 115 | Latency Distribution 116 | 50% 5.67ms 117 | 75% 6.92ms 118 | 90% 9.28ms 119 | 99% 14.78ms 120 | 1219733 requests in 15.10s, 150.06MB read 121 | Requests/sec: 80780.85 122 | Transfer/sec: 9.94MB 123 | STARTTIME 1543842126 124 | ENDTIME 1543842141 125 | 126 | --------------------------------------------------------- 127 | Concurrency: 512 for tokio 128 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 129 | --------------------------------------------------------- 130 | 131 | Running 15s test @ http://127.0.0.1:8080/plaintext 132 | 12 threads and 512 connections 133 | Thread Stats Avg Stdev Max +/- Stdev 134 | Latency 6.26ms 2.05ms 36.47ms 79.26% 135 | Req/Sec 6.78k 474.02 13.65k 81.26% 136 | Latency Distribution 137 | 50% 6.06ms 138 | 75% 7.27ms 139 | 90% 8.34ms 140 | 99% 12.18ms 141 | 1220289 requests in 15.10s, 150.12MB read 142 | Requests/sec: 80815.85 143 | Transfer/sec: 9.94MB 144 | STARTTIME 1543842143 145 | ENDTIME 1543842158 146 | ops@pressure:~/benchmark$ -------------------------------------------------------------------------------- /docs/logs/b2/Photon-empty.log: -------------------------------------------------------------------------------- 1 | ops@pressure:~/benchmark$ ./bench.sh Photon-empty plaintext 127.0.0.1 8080 2 | Usage: ./bench.sh Name plaintext 127.0.0.1 8080 3 | 4 | --------------------------------------------------------- 5 | Running Primer Photon-empty 6 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 5 -c 8 --timeout 8 -t 8 http://127.0.0.1:8080/plaintext 7 | --------------------------------------------------------- 8 | 9 | Running 5s test @ http://127.0.0.1:8080/plaintext 10 | 8 threads and 8 connections 11 | Thread Stats Avg Stdev Max +/- Stdev 12 | Latency 54.34us 406.62us 16.04ms 99.71% 13 | Req/Sec 26.73k 1.53k 31.20k 84.31% 14 | Latency Distribution 15 | 50% 33.00us 16 | 75% 40.00us 17 | 90% 47.00us 18 | 99% 63.00us 19 | 1084669 requests in 5.10s, 162.40MB read 20 | Requests/sec: 212708.68 21 | Transfer/sec: 31.85MB 22 | 23 | --------------------------------------------------------- 24 | Running Warmup Photon-empty 25 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 26 | --------------------------------------------------------- 27 | 28 | Running 15s test @ http://127.0.0.1:8080/plaintext 29 | 12 threads and 512 connections 30 | Thread Stats Avg Stdev Max +/- Stdev 31 | Latency 1.02ms 602.94us 22.67ms 93.43% 32 | Req/Sec 36.77k 5.43k 95.17k 65.98% 33 | Latency Distribution 34 | 50% 0.92ms 35 | 75% 1.10ms 36 | 90% 1.38ms 37 | 99% 2.47ms 38 | 6595344 requests in 15.10s, 0.96GB read 39 | Requests/sec: 436782.98 40 | Transfer/sec: 65.40MB 41 | 42 | --------------------------------------------------------- 43 | Concurrency: 512 for Photon-empty 44 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 45 | --------------------------------------------------------- 46 | 47 | Running 15s test @ http://127.0.0.1:8080/plaintext 48 | 12 threads and 512 connections 49 | Thread Stats Avg Stdev Max +/- Stdev 50 | Latency 1.04ms 742.42us 32.91ms 96.41% 51 | Req/Sec 36.41k 5.33k 99.93k 64.85% 52 | Latency Distribution 53 | 50% 0.94ms 54 | 75% 1.13ms 55 | 90% 1.40ms 56 | 99% 2.81ms 57 | 6528262 requests in 15.10s, 0.95GB read 58 | Requests/sec: 432361.89 59 | Transfer/sec: 64.74MB 60 | STARTTIME 1543927130 61 | ENDTIME 1543927145 62 | 63 | --------------------------------------------------------- 64 | Concurrency: 512 for Photon-empty 65 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 66 | --------------------------------------------------------- 67 | 68 | Running 15s test @ http://127.0.0.1:8080/plaintext 69 | 12 threads and 512 connections 70 | Thread Stats Avg Stdev Max +/- Stdev 71 | Latency 1.01ms 701.69us 22.92ms 95.05% 72 | Req/Sec 36.46k 5.37k 52.74k 60.83% 73 | Latency Distribution 74 | 50% 0.90ms 75 | 75% 1.10ms 76 | 90% 1.37ms 77 | 99% 3.65ms 78 | 6532374 requests in 15.02s, 0.96GB read 79 | Requests/sec: 434914.38 80 | Transfer/sec: 65.12MB 81 | STARTTIME 1543927147 82 | ENDTIME 1543927163 83 | 84 | --------------------------------------------------------- 85 | Concurrency: 512 for Photon-empty 86 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 87 | --------------------------------------------------------- 88 | 89 | Running 15s test @ http://127.0.0.1:8080/plaintext 90 | 12 threads and 512 connections 91 | Thread Stats Avg Stdev Max +/- Stdev 92 | Latency 1.04ms 494.13us 21.08ms 92.23% 93 | Req/Sec 35.93k 5.06k 96.20k 65.85% 94 | Latency Distribution 95 | 50% 0.96ms 96 | 75% 1.14ms 97 | 90% 1.39ms 98 | 99% 2.02ms 99 | 6441439 requests in 15.10s, 0.94GB read 100 | Requests/sec: 426589.69 101 | Transfer/sec: 63.87MB 102 | STARTTIME 1543927165 103 | ENDTIME 1543927180 104 | 105 | --------------------------------------------------------- 106 | Concurrency: 512 for Photon-empty 107 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 108 | --------------------------------------------------------- 109 | 110 | Running 15s test @ http://127.0.0.1:8080/plaintext 111 | 12 threads and 512 connections 112 | Thread Stats Avg Stdev Max +/- Stdev 113 | Latency 1.02ms 636.29us 29.67ms 94.24% 114 | Req/Sec 36.52k 5.29k 52.52k 60.94% 115 | Latency Distribution 116 | 50% 0.92ms 117 | 75% 1.10ms 118 | 90% 1.40ms 119 | 99% 2.65ms 120 | 6542307 requests in 15.02s, 0.96GB read 121 | Requests/sec: 435692.52 122 | Transfer/sec: 65.23MB 123 | STARTTIME 1543927182 124 | ENDTIME 1543927197 125 | 126 | --------------------------------------------------------- 127 | Concurrency: 512 for Photon-empty 128 | wrk -H 'Host: 127.0.0.1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Connection: keep-alive' --latency -d 15 -c 512 --timeout 8 -t 12 "http://127.0.0.1:8080/plaintext" 129 | --------------------------------------------------------- 130 | 131 | Running 15s test @ http://127.0.0.1:8080/plaintext 132 | 12 threads and 512 connections 133 | Thread Stats Avg Stdev Max +/- Stdev 134 | Latency 1.02ms 659.62us 22.65ms 94.14% 135 | Req/Sec 36.52k 5.29k 94.01k 63.35% 136 | Latency Distribution 137 | 50% 0.92ms 138 | 75% 1.11ms 139 | 90% 1.39ms 140 | 99% 2.97ms 141 | 6547118 requests in 15.10s, 0.96GB read 142 | Requests/sec: 433603.53 143 | Transfer/sec: 64.92MB 144 | STARTTIME 1543927199 145 | ENDTIME 1543927214 146 | ops@pressure:~/benchmark$ 147 | 148 | -------------------------------------------------------------------------------- /dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hunt", 3 | "description": "A refined core library for D programming language.", 4 | "copyright": "Copyright (C) 2018-2020, HuntLabs", 5 | "homepage": "https://www.huntlabs.net", 6 | "license": "Apache-2.0", 7 | "versions-osx": [ 8 | "HAVE_KQUEUE" 9 | ], 10 | "versions-freebsd": [ 11 | "HAVE_KQUEUE" 12 | ], 13 | "versions-dragonflybsd": [ 14 | "HAVE_KQUEUE" 15 | ], 16 | "versions-openbsd": [ 17 | "HAVE_KQUEUE" 18 | ], 19 | "versions-netbsd": [ 20 | "HAVE_KQUEUE" 21 | ], 22 | "versions-windows": [ 23 | "HAVE_IOCP" 24 | ], 25 | "versions-linux": [ 26 | "HAVE_EPOLL" 27 | ], 28 | "versions-android": [ 29 | "HAVE_EPOLL" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /examples/configuration/.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | docs/ 5 | configurationdemo.so 6 | configurationdemo.dylib 7 | configurationdemo.dll 8 | configurationdemo.a 9 | configurationdemo.lib 10 | configurationdemo-test-* 11 | *.exe 12 | *.o 13 | *.obj 14 | *.lst 15 | -------------------------------------------------------------------------------- /examples/configuration/application.conf: -------------------------------------------------------------------------------- 1 | hunt.application.name = MYSITE 2 | hunt.application.baseUrl = http://localhost:8080/ 3 | hunt.application.defaultCookieDomain = .example.com 4 | hunt.application.defaultLanguage = zh-CN 5 | hunt.application.languages = zh-CN,en-US 6 | hunt.application.secret = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 7 | hunt.application.encoding = UTF-8 8 | hunt.application.staticFileCacheMinutes = 30 9 | 10 | # hunt.route.groups = admin:directory:admincp, api:domain:api.example.com 11 | 12 | hunt.session.storage = file 13 | hunt.session.path = ./storage/sessions/ 14 | hunt.session.prefix = huntsession_ 15 | hunt.session.expire = 3600 16 | hunt.session.args = 17 | 18 | hunt.cache.storage = memory 19 | hunt.cache.prefix = huntcache_ 20 | hunt.cache.expire = 3600 21 | hunt.cache.args = 22 | hunt.cache.enableL2 = 23 | 24 | hunt.memcache.enabled = true 25 | hunt.memcache.servers = 127.0.0.1:11211 26 | 27 | hunt.redis.enabled = true 28 | hunt.redis.host = 127.0.0.1 29 | hunt.redis.port = 6379 30 | hunt.redis.database = 0 31 | hunt.redis.password = "" 32 | hunt.redis.timeout = 0 33 | 34 | hunt.http.address=0.0.0.0 35 | hunt.http.port=8080 36 | hunt.http.workerThreads=4 37 | hunt.http.cacheControl=0 38 | hunt.http.ioThreads = 39 | hunt.http.keepAliveTimeOut= 40 | 41 | hunt.https.enabled=true 42 | #hunt.https.privateKey=privkey.pem 43 | #hunt.https.certKey=cert.pem 44 | hunt.https.protocol = "TLS"; 45 | hunt.https.keyStore=keystore.p12 46 | hunt.https.keyStoreType=PKCS12 47 | hunt.https.keyStorePassword=secret 48 | 49 | 50 | hunt.log.level=DEBUG 51 | hunt.log.path= 52 | hunt.log.file= 53 | hunt.log.maxSize = 8M 54 | hunt.log.maxNum=10 55 | 56 | hunt.upload.path=attachments/ 57 | hunt.upload.maxSize=4096000 58 | 59 | hunt.cron.noon=0 0 12 * * ? 60 | 61 | hunt.date.format=yyyy-mm-dd 62 | hunt.date.timeZone=Asia/Shanghai 63 | 64 | hunt.database.default.driver=postgresql 65 | hunt.database.default.host=127.0.0.1 66 | hunt.database.default.port=2345 67 | hunt.database.default.database=test 68 | hunt.database.default.username=root 69 | hunt.database.default.password= 70 | hunt.database.default.charset=utf8 71 | hunt.database.default.prefix= 72 | hunt.database.default.enabled=false 73 | 74 | hunt.database.pool.name= 75 | hunt.database.pool.minIdle=5 76 | hunt.database.pool.idleTimeout=30000 77 | hunt.database.pool.maxConnection=20 78 | hunt.database.pool.minConnection=5 79 | hunt.database.pool.maxPoolSize=20 80 | hunt.database.pool.minPoolSize=20 81 | hunt.database.pool.maxLifetime=2000000 82 | hunt.database.pool.connectionTimeout=30000 83 | 84 | 85 | hunt.mail.smtp.host = smtp.gmail.com 86 | hunt.mail.smtp.channel = 87 | hunt.mail.smtp.port=2500 88 | hunt.mail.smtp.protocol=smtps 89 | hunt.mail.smtp.user = 90 | hunt.mail.smtp.password = 91 | 92 | hunt.view.path = ./views/ -------------------------------------------------------------------------------- /examples/configuration/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "configuration", 3 | "targetType": "executable", 4 | "authors": [ 5 | "Putao" 6 | ], 7 | "description": "A simple demo for hunt.util.Configuration", 8 | "copyright": "Copyright © 2017-2018, HuntLabs.net", 9 | "license": "Apache-2.0", 10 | "dependencies": { 11 | "hunt" : { 12 | "path" : "../../" 13 | } 14 | }, 15 | "versions": [ 16 | "HUNT_DEBUG_CONFIG" 17 | ], 18 | "dflags-posix":["-mixin=mixin_output.d"] 19 | } -------------------------------------------------------------------------------- /examples/configuration/source/app.d: -------------------------------------------------------------------------------- 1 | import std.array; 2 | import std.conv; 3 | import std.stdio; 4 | import std.file; 5 | import std.exception; 6 | import std.path; 7 | 8 | import hunt.util.Configuration; 9 | import hunt.logging; 10 | 11 | import settings; 12 | 13 | import core.time; 14 | 15 | void main() { 16 | writeln("===== testing application.conf =====\n"); 17 | testApplicationConfig(); 18 | 19 | writeln("\n===== testing test.config =====\n"); 20 | testConfig1(); 21 | 22 | writeln("\n===== testing test.config =====\n"); 23 | testConfig2(); 24 | 25 | writeln("\n===== testing Configuration Builder 1 =====\n"); 26 | testConfigBuilder1(); 27 | 28 | writeln("\n\n===== testing Configuration Builder 2 =====\n"); 29 | testConfigBuilder2(); 30 | 31 | writeln("\n\n===== testing Configuration Builder 3 =====\n"); 32 | testConfigBuilder3(); 33 | 34 | testArrayConfig(); 35 | 36 | } 37 | 38 | // Allow loading the configuration from some prefix in case we're 39 | // installed somewhere and not run in the repo. 40 | string convertConfigPathToRelative(string configName) { 41 | mixin("string confPrefix = \"@CONF_PREFIX@\";"); 42 | // We don't want meson to replace the CONF_PREFIX here too, 43 | // otherwise this would always be true. 44 | if (confPrefix == join(["@CONF", "_PREFIX@"])) { 45 | return configName; 46 | } else { 47 | auto relConfPath = relativePath(confPrefix, std.file.getcwd); 48 | return buildPath(relConfPath, configName); 49 | } 50 | } 51 | 52 | void testApplicationConfig() { 53 | ConfigBuilder manager; 54 | 55 | manager = new ConfigBuilder(convertConfigPathToRelative("application.conf")); 56 | 57 | // writeln("x======", manager.hunt.application.name); 58 | // FIXME: Needing refactor or cleanup -@zxp at 1/9/2019, 6:37:48 PM 59 | // 60 | // assert(manager.hunt.application.name == "MYSITE"); 61 | assert(manager.hunt.application.encoding.value() == "UTF-8"); 62 | assert(manager.hunt.application.encoding.value() == "UTF-8"); 63 | assert(manager.hunt.log.path.value().empty); 64 | assert(manager.hunt.mail.smtp.password.value().empty); 65 | 66 | AppConfig config = manager.build!(AppConfig)(); 67 | assert(config.view.path == "./views/", config.view.path); 68 | } 69 | 70 | void testConfig1() { 71 | ConfigBuilder conf = new ConfigBuilder(convertConfigPathToRelative("test.config")); 72 | 73 | assert(conf.app.node1.node2.node3.node4 is null); 74 | 75 | // assertThrown!(EmptyValueException)(conf.app.node1.node2.node3.node4.value()); 76 | 77 | // assert(conf.app.subItem("package").name.value() == "Hunt package"); // use keyword as a node name 78 | // assert(conf["app"]["package"]["name"].value() == "Hunt package"); // use keyword as a node name 79 | 80 | assert(conf.app.node1.node2.node3.value() == "nothing"); 81 | assert(conf.http.listen.value.as!long() == 100); 82 | assert(conf.app.buildMode.value() == "default"); 83 | assert(conf.app.time.value() == "0.25"); 84 | assert(conf.app.time.as() == "0.25"); 85 | assert(conf.app.time.as!float() == 0.25); 86 | string buildMode = conf.app.buildMode.value(); 87 | assert(buildMode == "default"); 88 | 89 | assert(conf.getProperty("name") == "GlobleConfiguration"); 90 | assert(conf.getProperty("name___x", "default") == "default"); 91 | assert(conf.getProperty("name___x").empty()); 92 | assert(conf.getProperty("app.buildMode") == "default"); 93 | 94 | } 95 | 96 | void testConfig2() { 97 | auto conf = new ConfigBuilder(convertConfigPathToRelative("test.config"), "dev"); 98 | assert(conf.http.listen.value.as!long() == 100); 99 | assert(conf.http.listen.as!long() == 100); 100 | string buildMode = conf.app.buildMode.value(); 101 | assert(buildMode == "dev"); 102 | assert(conf.getProperty("app.buildMode") == "dev"); 103 | } 104 | 105 | void testConfigBuilder1() { 106 | ConfigBuilder manager = new ConfigBuilder(convertConfigPathToRelative("test.config")); 107 | 108 | BuilderTest1Config config = manager.build!BuilderTest1Config(); 109 | assert(config.timeout.total!("msecs") == 234, config.timeout.toString()); 110 | 111 | writeln(manager.app.server1.ip.value()); 112 | writeln(manager.app.server1.port.value()); 113 | writeln(".................."); 114 | writeln(config.server1.ip); 115 | writeln(config.server1.port); 116 | 117 | assert(config.name == "GlobleConfiguration", config.name); 118 | assert(config.time == 2018, to!string(config.time)); 119 | 120 | assert(config.interval1 == 500, to!string(config.interval1)); 121 | assert(config.interval2 == 600, to!string(config.interval2)); 122 | assert(config.interval3 == 700, to!string(config.interval3)); 123 | 124 | assert(config.server1.ip == "8.8.6.1", config.server2.ip); 125 | assert(config.server1.port == 81, to!string(config.server2.port)); 126 | 127 | assert(config.server2.ip == "127.0.0.1", config.server2.ip); 128 | assert(config.server2.port == 8080, to!string(config.server2.port)); 129 | } 130 | 131 | void testConfigBuilder2() { 132 | ConfigBuilder manager = new ConfigBuilder(convertConfigPathToRelative("test.config")); 133 | 134 | TestConfig config = manager.build!(TestConfig, "app")(); 135 | 136 | // assert(config.package1 !is null); 137 | // assert(config.package1.name == "Hunt package", config.package1.name); 138 | // assert(config.package2 !is null); 139 | // assert(config.package2.name == "Hunt pkg", config.package2.name); 140 | 141 | assert(config.name == "Hunt-dev", config.name); 142 | assert(config.time == 0.25, to!string(config.time)); 143 | 144 | assert(config.interval1 == 550, to!string(config.time)); 145 | assert(config.interval2 == 550, to!string(config.time)); 146 | assert(config.interval3 == 888, to!string(config.time)); 147 | 148 | assert(config.server1.ip == "8.8.7.1", config.server1.ip); 149 | assert(config.server1.port == 8071, to!string(config.server1.port)); 150 | 151 | assert(config.server2.ip == "8.8.10.1", config.server2.ip); 152 | assert(config.server2.port == 8081, to!string(config.server2.port)); 153 | 154 | assert(config.description == "Shanghai Puto Inc.", config.description); 155 | } 156 | 157 | void testConfigBuilder3() { 158 | ConfigBuilder manager = new ConfigBuilder(convertConfigPathToRelative("test.config")); 159 | 160 | TestConfigEx config = manager.build!(TestConfigEx, "app")(); 161 | 162 | writeln(config.fullName); 163 | writeln(config.name); 164 | writeln(config.time); 165 | } 166 | 167 | 168 | void testArrayConfig() { 169 | 170 | writeln("\n\n===== testing Array Config =====\n"); 171 | ConfigBuilder manager = new ConfigBuilder(convertConfigPathToRelative("test2.config")); 172 | 173 | assert(manager["name"].value() == "Test ArrayConfig"); 174 | assert(manager["servers[0]"]["port"].value() == "81"); 175 | assert(manager["ages"].value() == "20, 30, 40"); 176 | assert(manager["users"].value() == "user01, user02, user03"); 177 | 178 | // 179 | ArrayTestConfig config = manager.build!(ArrayTestConfig)(); 180 | 181 | assert(config.name == "Test ArrayConfig"); 182 | assert(config.ages == [20, 30, 40]); 183 | assert(config.users == ["user01", "user02", "user03"]); 184 | 185 | ServerSettings[] servers = config.servers; 186 | assert(servers.length == 2); 187 | assert(servers[0].ip == "8.8.6.1"); 188 | assert(servers[1].port == 82); 189 | } 190 | -------------------------------------------------------------------------------- /examples/configuration/source/settings.d: -------------------------------------------------------------------------------- 1 | module settings; 2 | 3 | import hunt.util.Configuration; 4 | import core.time; 5 | 6 | @Configuration("http") 7 | struct TestHttpConfig 8 | { 9 | @Value("listen") 10 | int value; 11 | 12 | string addr; 13 | } 14 | 15 | @Configuration("server") 16 | struct ServerSettings 17 | { 18 | @Value("listen") 19 | string ip = "127.0.0.1"; 20 | 21 | ushort port = 8080; 22 | } 23 | 24 | 25 | @Configuration("package") 26 | class PackageSettings 27 | { 28 | string name; 29 | } 30 | 31 | @Configuration("app") 32 | class TestConfig 33 | { 34 | string name = "Hunt"; 35 | 36 | @Value("time") 37 | double time; 38 | 39 | PackageSettings package1; 40 | 41 | @Value("pkg") 42 | PackageSettings package2; 43 | 44 | ServerSettings server1; 45 | 46 | @Value("httpserver") 47 | ServerSettings server2; 48 | 49 | @Value("interval", true) 50 | int interval1 = 500; 51 | 52 | @Value("interval", true) 53 | int interval2 = 600; 54 | 55 | int interval3 = 700; 56 | 57 | @property void description(string d) 58 | { 59 | _desc = d; 60 | } 61 | 62 | @property string description() 63 | { 64 | return _desc; 65 | } 66 | 67 | private string _desc = "Putao Ltd."; 68 | 69 | } 70 | 71 | 72 | class BuilderTest1Config 73 | { 74 | string name = "Hunt"; 75 | 76 | @Value("time") 77 | double time; 78 | 79 | ServerSettings server1; 80 | 81 | @Value("httpserver") 82 | ServerSettings server2; 83 | 84 | @Value("interval", true) 85 | int interval1 = 500; 86 | 87 | @Value("interval", true) 88 | int interval2 = 600; 89 | 90 | int interval3 = 700; 91 | 92 | 93 | Duration timeout; 94 | 95 | } 96 | 97 | 98 | class ArrayTestConfig { 99 | string name; 100 | int[] ages; 101 | string[] users; 102 | ServerSettings[] servers; 103 | } 104 | 105 | class TestConfigEx : TestConfig 106 | { 107 | string fullName = "Putao"; 108 | } 109 | 110 | 111 | @Configuration("hunt") 112 | class AppConfig 113 | { 114 | struct ApplicationConf 115 | { 116 | string name = "HUNT APPLICATION"; 117 | string baseUrl; 118 | string defaultCookieDomain = ".example.com"; 119 | string defaultLanguage = "zh-CN"; 120 | string languages = "zh-CN,en-US"; 121 | string secret = "CD6CABB1123C86EDAD9"; 122 | string encoding = "utf-8"; 123 | int staticFileCacheMinutes = 30; 124 | } 125 | 126 | struct SessionConf 127 | { 128 | string storage = "memory"; 129 | string prefix = "huntsession_"; 130 | string args = "/tmp"; 131 | uint expire = 3600; 132 | } 133 | 134 | struct CacheConf 135 | { 136 | string storage = "memory"; 137 | string args = "/tmp"; 138 | bool enableL2 = false; 139 | } 140 | 141 | struct HttpConf 142 | { 143 | string address = "0.0.0.0"; 144 | ushort port = 8080; 145 | uint workerThreads = 4; 146 | uint ioThreads = 2; 147 | size_t keepAliveTimeOut = 30; 148 | size_t maxHeaderSize = 60 * 1024; 149 | int cacheControl; 150 | string path; 151 | } 152 | 153 | struct HttpsConf 154 | { 155 | bool enabled = false; 156 | string protocol; 157 | string keyStore; 158 | string keyStoreType; 159 | string keyStorePassword; 160 | } 161 | 162 | struct RouteConf 163 | { 164 | string groups; 165 | } 166 | 167 | struct LogConfig 168 | { 169 | string level = "all"; 170 | string path; 171 | string file = ""; 172 | bool disableConsole = false; 173 | string maxSize = "8M"; 174 | uint maxNum = 8; 175 | } 176 | 177 | struct MemcacheConf 178 | { 179 | bool enabled = false; 180 | string servers; 181 | } 182 | 183 | struct RedisConf 184 | { 185 | bool enabled = false; 186 | string host = "127.0.0.1"; 187 | string password = ""; 188 | ushort database = 0; 189 | ushort port = 6379; 190 | uint timeout = 0; 191 | } 192 | 193 | struct UploadConf 194 | { 195 | string path; 196 | uint maxSize = 4 * 1024 * 1024; 197 | } 198 | 199 | struct DownloadConfig 200 | { 201 | string path = "downloads"; 202 | } 203 | 204 | struct MailSmtpConf 205 | { 206 | string host; 207 | string channel; 208 | ushort port; 209 | string protocol; 210 | string user; 211 | string password; 212 | } 213 | 214 | struct MailConf 215 | { 216 | MailSmtpConf smtp; 217 | } 218 | 219 | struct DbPoolConf 220 | { 221 | uint maxConnection = 10; 222 | uint minConnection = 10; 223 | uint timeout = 10000; 224 | } 225 | 226 | struct DBConfig 227 | { 228 | string url; 229 | DbPoolConf pool; 230 | } 231 | 232 | struct DateConf 233 | { 234 | string format; 235 | string timeZone; 236 | } 237 | 238 | struct CornConf 239 | { 240 | string noon; 241 | } 242 | 243 | struct ServiceConf 244 | { 245 | string address = "127.0.0.1"; 246 | ushort port; 247 | int workerThreads; 248 | string password; 249 | } 250 | 251 | struct RpcConf 252 | { 253 | bool enabled = true; 254 | ServiceConf service; 255 | } 256 | 257 | struct Views 258 | { 259 | string path = "views/"; 260 | string ext = ".dhtml"; 261 | } 262 | 263 | DBConfig database; 264 | ApplicationConf application; 265 | SessionConf session; 266 | CacheConf cache; 267 | HttpConf http; 268 | HttpsConf https; 269 | RouteConf route; 270 | MemcacheConf memcache; 271 | RedisConf redis; 272 | LogConfig log; 273 | UploadConf upload; 274 | DownloadConfig download; 275 | CornConf cron; 276 | DateConf date; 277 | MailConf mail; 278 | RpcConf rpc; 279 | Views view; 280 | } 281 | -------------------------------------------------------------------------------- /examples/configuration/test.config: -------------------------------------------------------------------------------- 1 | http.listen = 100 2 | 3 | name = GlobleConfiguration 4 | time = 2018 5 | 6 | # milliseconds 7 | timeout = 234 8 | 9 | TestConfig.name = Hunt-config 10 | TestConfig.time = 1.31 11 | 12 | server.ip = 8.8.6.0 13 | server.listen = 8.8.6.1 14 | server.port = 81 15 | 16 | app.node1.node2.node3 = nothing 17 | app.name = Hunt-dev 18 | app.description = Shanghai Puto Inc. 19 | app.fullName = Putao Hunt 20 | app.time = 0.25 21 | app.interval = 550 22 | app.interval3 = 888 23 | app.buildMode = default 24 | 25 | app.package.name = "Hunt package" 26 | app.pkg.name = "Hunt pkg" 27 | 28 | app.server.ip = 8.8.7.0 29 | app.server.listen = 8.8.7.1 30 | app.server.port = 8071 31 | 32 | app.server1.ip = 8.8.8.0 33 | app.server1.listen = 8.8.8.1 34 | app.server1.port = 80 35 | 36 | app.server2.ip = 8.8.9.0 37 | app.server2.listen = 8.8.9.1 38 | 39 | app.httpserver.ip = 8.8.10.0 40 | app.httpserver.listen = 8.8.10.1 41 | app.httpserver.port = 8081 42 | 43 | # this is 44 | ; start dev 45 | 46 | [dev] 47 | app.buildMode = dev 48 | 49 | [release] 50 | app.buildMode = release 51 | -------------------------------------------------------------------------------- /examples/configuration/test2.config: -------------------------------------------------------------------------------- 1 | 2 | name = Test ArrayConfig 3 | 4 | servers[0].listen = 8.8.6.1 5 | servers[0].port = 81 6 | 7 | servers[1].listen = 8.8.6.2 8 | servers[1].port = 82 9 | 10 | ages = 20, 30, 40 11 | users = user01, user02, user03 12 | -------------------------------------------------------------------------------- /examples/logger/.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | docs/ 5 | *.exe 6 | *.o 7 | *.obj 8 | *.lst 9 | logger-demo -------------------------------------------------------------------------------- /examples/logger/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "logger-demo", 3 | "authors": [ 4 | "Putao" 5 | ], 6 | "description": "A simple demo for hunt.logging.", 7 | "copyright": "Copyright © 2017-2018, HuntLabs.net", 8 | "license": "Apache-2.0", 9 | "dependencies": { 10 | "hunt" : { 11 | "path" : "../../" 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /examples/logger/source/app.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A refined core library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | import std.stdio; 13 | import hunt.logging.Logger; 14 | //import hunt.logging; 15 | import hunt.util.DateTime; 16 | import std.datetime; 17 | 18 | void main() { 19 | hunt.util.DateTime.DateTime.startClock(); 20 | 21 | Logger.setLogLevel(LogLevel.LOG_DEBUG); 22 | LogConf conf; 23 | conf.disableConsole = true; 24 | conf.level = LogLevel.LOG_INFO; 25 | conf.fileName = "test.log"; 26 | conf.maxSize = "2K"; 27 | conf.maxNum = 10; 28 | logLoadConf(conf); 29 | LogConf conf2; 30 | conf2.disableConsole = false; 31 | conf2.level = LogLevel.LOG_DEBUG; 32 | conf2.fileName = "hunt.log"; 33 | conf2.maxSize = "2K"; 34 | conf2.maxNum = 10; 35 | Logger log = Logger.createLogger("hunt",conf2); 36 | log.logLayoutHandler((string time_prior, string tid, string level, string myFunc, 37 | string msg, string file, size_t line) { 38 | import std.format; 39 | return format("%s | %s | %s | %s | %s | %s:%d", time_prior, tid, level, myFunc, msg, file, line);}); 40 | 41 | import std.json; 42 | setLogLayout((string time_prior, string tid, string level, string myFunc, 43 | string msg, string file, size_t line) { 44 | 45 | SysTime now = Clock.currTime; 46 | 47 | std.datetime.DateTime dt ; 48 | 49 | JSONValue jv; 50 | jv["timestamp"] = (cast(std.datetime.DateTime)now).toISOExtString(); 51 | jv["msecs"] = now.fracSecs.total!("msecs"); 52 | jv["level"] = level; 53 | jv["file"] = file; 54 | jv["module"] = myFunc; 55 | jv["funcName"] = myFunc; 56 | jv["line"] = line; 57 | jv["thread"] = tid; 58 | jv["threadName"] = tid; 59 | jv["process"] = 523; 60 | jv["message"] = msg; 61 | return jv.toPrettyString(); 62 | }); 63 | 64 | import core.thread; 65 | import core.time; 66 | 67 | // while(true) { 68 | // logDebugf("%s %s %d %d ", "test", "test1", 12, 13); 69 | // Thread.sleep(500.msecs); 70 | // } 71 | 72 | logDebug("test", " test1 ", "test2", conf); 73 | logDebugf("%s %s %d %d ", "test", "test1", 12, 13); 74 | trace("trace"); 75 | logInfo("info"); 76 | warning("warning"); 77 | error("error"); 78 | error("Chinese message: 错误"); 79 | 80 | log.info("info"); 81 | log.error("error"); 82 | } 83 | 84 | 85 | // void main() { 86 | // hunt.util.DateTime.DateTime.startClock(); 87 | 88 | 89 | // import std.json; 90 | // setLogLayout((string time_prior, string tid, string level, string myFunc, 91 | // string msg, string file, size_t line) { 92 | 93 | // SysTime now = Clock.currTime; 94 | 95 | // std.datetime.DateTime dt; 96 | 97 | // JSONValue jv; 98 | // jv["timestamp"] = now.toSimpleString(); 99 | // jv["level"] = level; 100 | // jv["file"] = file; 101 | // jv["module"] = myFunc; 102 | // jv["funcName"] = myFunc; 103 | // jv["line"] = line; 104 | // jv["thread"] = tid; 105 | // jv["message"] = msg; 106 | // return jv.toPrettyString(); 107 | // }); 108 | 109 | 110 | // logDebugf("%s %s %d %d ", "test", "test1", 12, 13); 111 | // trace("trace"); 112 | // logInfo("info"); 113 | // warning("warning"); 114 | // error("error"); 115 | // error("Chinese message: 错误"); 116 | // } -------------------------------------------------------------------------------- /examples/performance/.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | docs/ 5 | demo.so 6 | demo.dylib 7 | demo.dll 8 | demo.a 9 | demo.lib 10 | demo-test-* 11 | *.exe 12 | *.o 13 | *.obj 14 | *.lst 15 | -------------------------------------------------------------------------------- /examples/performance/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(Windows) Launch", 9 | "type": "cppvsdbg", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/performance.exe", 12 | "args": [], 13 | "stopAtEntry": false, 14 | "cwd": "${workspaceFolder}", 15 | "environment": [], 16 | "externalConsole": true 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /examples/performance/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "performance", 3 | "targetType": "executable", 4 | "description": "A performance tester for Hunt.", 5 | "copyright": "Copyright © 2017-2019, HuntLabs.net", 6 | "license": "Apache-2.0", 7 | "versions": ["HUNT_DEBUG1", "HUNT_IO_DEBUG1"], 8 | "dependencies": { 9 | "hunt" : { 10 | "path" : "../../" 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /examples/tcp/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(Windows) launch", 9 | "type": "cppvsdbg", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/tcp-server.exe", 12 | "args": [], 13 | "stopAtEntry": false, 14 | "cwd": "${workspaceFolder}", 15 | "environment": [], 16 | "externalConsole": false 17 | }, 18 | { 19 | "name": "(gdb) launch-server", 20 | "type": "cppdbg", 21 | "request": "launch", 22 | "program": "${workspaceFolder}/tcp-server", 23 | "args": [], 24 | "stopAtEntry": false, 25 | "cwd": "${workspaceFolder}", 26 | "environment": [], 27 | "externalConsole": false, 28 | "MIMode": "gdb", 29 | "setupCommands": [ 30 | { 31 | "description": "gdb", 32 | "text": "-enable-pretty-printing", 33 | "ignoreFailures": true 34 | } 35 | ] 36 | } 37 | ] 38 | } -------------------------------------------------------------------------------- /examples/tcp/build.bat: -------------------------------------------------------------------------------- 1 | dub build --compiler=dmd -a=x86_64 -b=debug -c=client 2 | dub build --compiler=dmd -a=x86_64 -b=debug -c=server 3 | -------------------------------------------------------------------------------- /examples/tcp/build.sh: -------------------------------------------------------------------------------- 1 | dub build --compiler=dmd -a=x86_64 -b=debug -c=client 2 | dub build --compiler=dmd -a=x86_64 -b=debug -c=server 3 | -------------------------------------------------------------------------------- /examples/tcp/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tcp-demo", 3 | "description": "A demo for TCP server/client with Hunt.", 4 | "copyright": "Copyright (C) 2018-2019, HuntLabs.net", 5 | "license": "Apache-2.0", 6 | "dependencies": { 7 | "hunt" :{"path": "../../"} 8 | }, 9 | "versions": [ 10 | "HUNT_DEBUG" 11 | ], 12 | "configurations": [ 13 | { 14 | "name": "client", 15 | "targetName": "tcp-client", 16 | "targetType": "executable", 17 | "excludedSourceFiles": [ 18 | "source/server.d" 19 | ] 20 | }, 21 | { 22 | "name": "server", 23 | "targetName": "tcp-server", 24 | "targetType": "executable", 25 | "excludedSourceFiles": [ 26 | "source/client.d" 27 | ] 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /examples/tcp/source/client.d: -------------------------------------------------------------------------------- 1 | import hunt.io.ByteBuffer; 2 | import hunt.event; 3 | import hunt.io.TcpStream; 4 | import hunt.io.IoError; 5 | import hunt.logging; 6 | 7 | import core.thread; 8 | import core.time; 9 | import std.parallelism; 10 | import std.socket; 11 | import std.stdio; 12 | 13 | enum Host = "127.0.0.1"; 14 | // enum Host = "10.1.222.110"; 15 | enum Port = 8080; 16 | 17 | void main() { 18 | trace("Start test..."); 19 | EventLoop loop = new EventLoop(); 20 | 21 | TcpStream client = new TcpStream(loop); 22 | int count = 10; 23 | client.connected((bool isSucceeded) { 24 | if (isSucceeded) { 25 | writeln("connected with: ", client.remoteAddress.toString()); 26 | string data = "Hello world!"; 27 | debug writeln("sending: size=", data.length, " content: ", data); 28 | client.write(cast(const(ubyte[])) data); 29 | } else { 30 | warning("The connection failed!"); 31 | auto runTask = task(() { 32 | Thread.sleep(client.options.retryInterval); 33 | client.reconnect(); 34 | }); 35 | taskPool.put(runTask); 36 | } 37 | }).received((ByteBuffer buffer) { 38 | byte[] data = buffer.getRemaining(); 39 | writeln("received data: ", cast(string) data); 40 | if (--count > 0) { 41 | debug writeln("sending: size=", data.length, " content: ", cast(string) data); 42 | client.write(cast(ubyte[]) data); 43 | } else { 44 | client.close(); 45 | } 46 | 47 | return DataHandleStatus.Done; 48 | }).closed(() { 49 | writeln("The connection closed!"); loop.stop(); 50 | }).error((IoError error) { 51 | writefln("error occurred: %d %s", error.errorCode, error.errorMsg); 52 | }).connect(Host, Port); 53 | 54 | loop.run(100); 55 | } 56 | -------------------------------------------------------------------------------- /examples/tcp/source/server.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A refined core library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | import std.stdio; 13 | 14 | import hunt.io.ByteBuffer; 15 | import hunt.util.ThreadHelper; 16 | import hunt.util.TaskPool; 17 | import hunt.event; 18 | import hunt.io.TcpListener; 19 | import hunt.io.TcpStream; 20 | import hunt.io.IoError; 21 | import hunt.logging; 22 | import hunt.util.Timer; 23 | 24 | import std.socket; 25 | import std.functional; 26 | import std.exception; 27 | import std.datetime; 28 | import std.process; 29 | 30 | import core.thread; 31 | 32 | void main() { 33 | 34 | debug writefln("Main thread: %s", getTid()); 35 | 36 | EventLoop loop = new EventLoop(); 37 | TcpListener listener = new TcpListener(loop, AddressFamily.INET, 512); 38 | 39 | // dfmt off 40 | listener 41 | .error((IoError error) { 42 | writefln("error occurred: %d %s", error.errorCode, error.errorMsg); 43 | }) 44 | .bind(8080) 45 | .listen(1024) 46 | .accepted((TcpListener sender, TcpStream client) { 47 | debug writefln("new connection from: %s", client.remoteAddress.toString()); 48 | client.received((ByteBuffer buffer) { 49 | ubyte[] data = cast(ubyte[]) buffer.peekRemaining(); 50 | debug writeln("received bytes: ", data.length); 51 | const(ubyte)[] sentData = data; // echo test 52 | client.write(sentData); 53 | return DataHandleStatus.Done; 54 | }).disconnected(() { 55 | debug writefln("client disconnected: %s", client.remoteAddress.toString()); 56 | }).closed(() { 57 | debug writefln("connection closed, local: %s, remote: %s", 58 | client.localAddress.toString(), client.remoteAddress.toString()); 59 | try { 60 | client.write([0x41, 0x42, 0x43]); 61 | client.write([0x46, 0x47, 0x48, 0x49]); 62 | } catch(Exception ex) { 63 | warning(ex); 64 | } 65 | }).error((IoError error) { 66 | writefln("error occurred: %d %s", error.errorCode, error.errorMsg); 67 | }); 68 | }) 69 | .start(); 70 | 71 | // dfmt on 72 | 73 | writeln("Listening on: ", listener.bindingAddress.toString()); 74 | loop.run(); 75 | } 76 | -------------------------------------------------------------------------------- /examples/timer/.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | docs/ 5 | timer.so 6 | timer.dylib 7 | timer.dll 8 | timer.a 9 | timer.lib 10 | timer-test-* 11 | *.exe 12 | *.o 13 | *.obj 14 | *.lst 15 | -------------------------------------------------------------------------------- /examples/timer/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(gdb) Launch", 9 | "type": "cppdbg", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/timer", 12 | "args": [], 13 | "stopAtEntry": false, 14 | "cwd": "${workspaceFolder}", 15 | "environment": [], 16 | "externalConsole": true, 17 | "MIMode": "gdb", 18 | "setupCommands": [ 19 | { 20 | "description": "Enable pretty-printing for gdb", 21 | "text": "-enable-pretty-printing", 22 | "ignoreFailures": true 23 | } 24 | ] 25 | }, 26 | { 27 | "name": "(Windows) Launch", 28 | "type": "cppvsdbg", 29 | "request": "launch", 30 | "program": "${workspaceFolder}/timer.exe", 31 | "args": [], 32 | "stopAtEntry": false, 33 | "cwd": "${workspaceFolder}", 34 | "environment": [], 35 | "externalConsole": true 36 | } 37 | ] 38 | } -------------------------------------------------------------------------------- /examples/timer/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "timer", 3 | "copyright": "Copyright (C) 2018-2019, HuntLabs", 4 | "homepage": "https://www.huntlabs.net/", 5 | "description": "A timer example for Hunt.", 6 | "license": "Apache-2.0", 7 | "dependencies": { 8 | "hunt": { 9 | "path": "../../" 10 | } 11 | }, 12 | "versions": ["HUNT_DEBUG"], 13 | "configurations": [ 14 | { 15 | "name": "default", 16 | "targetType": "executable" 17 | }, 18 | { 19 | "name": "native", 20 | "targetType": "executable", 21 | "versions": [ 22 | "NativeTimer" 23 | ] 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /examples/timer/source/app.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A refined core library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | import std.stdio; 13 | 14 | import hunt.event.timer.Common; 15 | import hunt.Exceptions; 16 | import hunt.logging; 17 | import hunt.util.Timer; 18 | import hunt.util.DateTime; 19 | 20 | import core.thread; 21 | import core.time; 22 | 23 | version (NativeTimer) { 24 | void main() { 25 | int count1 = 10; 26 | int count2 = 6; 27 | 28 | void onTimerTick(Object sender) { 29 | trace("Countdown[1]: ", count1--); 30 | if (count1 == 0) { 31 | ITimer timer = cast(ITimer) sender; 32 | timer.stop(); 33 | } 34 | else if (count1 == 5) { 35 | trace("reset timer1's interval to 2 secondes"); 36 | ITimer timer = cast(ITimer) sender; 37 | timer.reset(2.seconds); 38 | } 39 | } 40 | 41 | new NativeTimer(1.seconds).onTick(&onTimerTick).start(); 42 | 43 | new NativeTimer().interval(2.seconds).onTick(delegate void(Object sender) { 44 | trace("Countdown[2]: ", count2--); 45 | if (count2 == 0) { 46 | ITimer timer = cast(ITimer) sender; 47 | timer.stop(); 48 | } 49 | }).start(); 50 | 51 | writeln("\r\nHit return to exit. \r\n"); 52 | getchar(); 53 | } 54 | 55 | } else { 56 | 57 | import hunt.event; 58 | 59 | void main() { 60 | DateTime.startClock(); 61 | EventLoop loop = new EventLoop(); 62 | 63 | bool isTimer1Running = true; 64 | bool isTimer2Running = true; 65 | 66 | void checkTimer() { 67 | if (isTimer1Running || isTimer2Running) 68 | return; 69 | // FIXME: Needing refactor or cleanup -@putao at 1/10/2019, 6:21:11 PM 70 | // Can't exit nicely 71 | writeln("\r\nAll timers stopped (hit return to exit)"); 72 | getchar(); 73 | loop.stop(); 74 | } 75 | 76 | int count1 = 10; 77 | void onTimerTick(Object sender) { 78 | trace("Countdown[1]: ", count1--); 79 | if (count1 == 0) { 80 | ITimer timer = cast(ITimer) sender; 81 | timer.stop(); 82 | isTimer1Running = false; 83 | checkTimer(); 84 | } 85 | else if (count1 == 5) { 86 | trace("reset the timer1's interval to 2 secondes"); 87 | ITimer timer = cast(ITimer) sender; 88 | timer.reset(2.seconds); 89 | } 90 | } 91 | 92 | new Timer(loop, 200.msecs).onTick(&onTimerTick).start(); 93 | 94 | int count2 = 5; 95 | new Timer(loop).interval(2.seconds).onTick(delegate void(Object sender) { 96 | trace("Countdown[2]: ", count2--); 97 | if (count2 == 0) { 98 | ITimer timer = cast(ITimer) sender; 99 | timer.stop(); 100 | isTimer2Running = false; 101 | checkTimer(); 102 | } 103 | }).start(); 104 | 105 | loop.run(100); 106 | 107 | thread_joinAll(); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /examples/udp/.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | docs/ 5 | udp-demo.so 6 | udp-demo.dylib 7 | udp-demo.dll 8 | udp-demo.a 9 | udp-demo.lib 10 | udp-demo-test-* 11 | *.exe 12 | *.o 13 | *.obj 14 | *.lst 15 | -------------------------------------------------------------------------------- /examples/udp/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(gdb) Launch", 9 | "type": "cppdbg", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/udp-demo", 12 | "args": [], 13 | "stopAtEntry": false, 14 | "cwd": "${workspaceFolder}", 15 | "environment": [], 16 | "externalConsole": true, 17 | "MIMode": "gdb", 18 | "setupCommands": [ 19 | { 20 | "description": "Enable pretty-printing for gdb", 21 | "text": "-enable-pretty-printing", 22 | "ignoreFailures": true 23 | } 24 | ] 25 | }, 26 | { 27 | "name": "(Windows) Launch", 28 | "type": "cppvsdbg", 29 | "request": "launch", 30 | "program": "${workspaceFolder}/udp-demo.exe", 31 | "args": [], 32 | "stopAtEntry": false, 33 | "cwd": "${workspaceFolder}", 34 | "environment": [], 35 | "externalConsole": true 36 | } 37 | ] 38 | } -------------------------------------------------------------------------------- /examples/udp/build.bat: -------------------------------------------------------------------------------- 1 | dub build --compiler=dmd -a=x86_64 -b=debug -c=client 2 | dub build --compiler=dmd -a=x86_64 -b=debug -c=server 3 | -------------------------------------------------------------------------------- /examples/udp/build.sh: -------------------------------------------------------------------------------- 1 | dub build --compiler=dmd -a=x86_64 -b=debug -c=client 2 | dub build --compiler=dmd -a=x86_64 -b=debug -c=server 3 | -------------------------------------------------------------------------------- /examples/udp/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "udp-demo", 3 | "description": "A demo for UDP server and client.", 4 | "copyright": "Copyright (C) 2018-2019, HuntLabs.net", 5 | "license": "Apache-2.0", 6 | "dependencies": { 7 | "hunt" :{"path": "../../"} 8 | }, 9 | "versions": [ 10 | "HUNT_DEBUG","HUNT_IO_DEBUG", "HUNT_IO_DEBUG_MORE" 11 | ], 12 | "configurations": [ 13 | { 14 | "name": "client", 15 | "targetName": "udp-client", 16 | "targetType": "executable", 17 | "excludedSourceFiles": [ 18 | "source/server.d" 19 | ] 20 | }, 21 | { 22 | "name": "server", 23 | "targetName": "udp-server", 24 | "targetType": "executable", 25 | "excludedSourceFiles": [ 26 | "source/client.d" 27 | ] 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /examples/udp/source/client.d: -------------------------------------------------------------------------------- 1 | module client; 2 | 3 | /* 4 | * Hunt - A refined core library for D programming language. 5 | * 6 | * Copyright (C) 2018-2019 HuntLabs 7 | * 8 | * Website: https://www.huntlabs.net/ 9 | * 10 | * Licensed under the Apache-2.0 License. 11 | * 12 | */ 13 | 14 | import std.stdio; 15 | 16 | import hunt.event; 17 | import hunt.io.UdpSocket : UdpSocket; 18 | 19 | import std.socket; 20 | import std.functional; 21 | import std.exception; 22 | import core.thread; 23 | import core.time; 24 | 25 | // http://www.cs.ubbcluj.ro/~dadi/compnet/labs/lab3/udp-broadcast.html 26 | void main() 27 | { 28 | EventLoop loop = new EventLoop(); 29 | 30 | // UDP Client 31 | UdpSocket udpClient = new UdpSocket(loop); 32 | 33 | int count = 3; 34 | Address address = new InternetAddress(InternetAddress.ADDR_ANY, InternetAddress.PORT_ANY); 35 | udpClient.enableBroadcast(true) 36 | .bind(address) 37 | // .bind("10.1.222.120", InternetAddress.PORT_ANY) 38 | .onReceived((in ubyte[] data, Address addr) { 39 | debug writefln("Client => count=%d, server: %s, received: %s", count, 40 | addr, cast(string) data); 41 | if (--count > 0) 42 | { 43 | udpClient.sendTo(data, addr); 44 | } 45 | else 46 | { 47 | udpClient.sendTo(cast(const(void)[]) "bye!", addr); 48 | udpClient.close(); 49 | // loop.stop(); 50 | } 51 | }) 52 | .start(); 53 | 54 | udpClient.sendTo(cast(const(void)[]) "Hello world!", parseAddress("255.255.255.255", 8080)); 55 | // udpClient.sendTo(cast(const(void)[]) "Hello world!", parseAddress("127.0.0.1", 8090)); 56 | // udpClient.sendTo(cast(const(void)[]) "Hello world!", parseAddress("10.1.222.120", 8080)); 57 | 58 | loop.run(); 59 | } 60 | -------------------------------------------------------------------------------- /examples/udp/source/server.d: -------------------------------------------------------------------------------- 1 | module server; 2 | 3 | /* 4 | * Hunt - A refined core library for D programming language. 5 | * 6 | * Copyright (C) 2018-2019 HuntLabs 7 | * 8 | * Website: https://www.huntlabs.net/ 9 | * 10 | * Licensed under the Apache-2.0 License. 11 | * 12 | */ 13 | 14 | import std.stdio; 15 | 16 | import hunt.event; 17 | import hunt.io.UdpSocket : UdpSocket; 18 | 19 | import std.socket; 20 | import std.functional; 21 | import std.exception; 22 | import core.thread; 23 | import core.time; 24 | 25 | void main() 26 | { 27 | EventLoop loop = new EventLoop(); 28 | 29 | // UDP Server 30 | UdpSocket udpSocket = new UdpSocket(loop); 31 | 32 | udpSocket.bind("0.0.0.0", 8080).onReceived((in ubyte[] data, Address addr) { 33 | debug writefln("Server => client: %s, received: %s", addr, cast(string) data); 34 | // if (data == "bye!") 35 | // { 36 | // udpSocket.close(); 37 | // // FIXME: Needing refactor or cleanup -@zxp at 4/25/2018, 10:17:32 AM 38 | // // The evenloop should be stopped nicely. 39 | // // loop.stop(); 40 | // } 41 | // else 42 | udpSocket.sendTo(data, addr); 43 | }).start(); 44 | 45 | writeln("Listening on (UDP): ", udpSocket.bindAddr.toString()); 46 | 47 | loop.run(); 48 | } 49 | -------------------------------------------------------------------------------- /examples/worker/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "worker", 3 | "targetType": "executable", 4 | "copyright": "Copyright (C) 2018-2020, HuntLabs", 5 | "homepage": "https://www.huntlabs.net", 6 | "description": "A simple demo for hunt.util.worker", 7 | "license": "Apache-2.0", 8 | "dependencies": { 9 | "hunt" : { 10 | "path" : "../../" 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /examples/worker/source/app.d: -------------------------------------------------------------------------------- 1 | import std.stdio; 2 | 3 | import core.thread; 4 | import core.time; 5 | 6 | import std.parallelism; 7 | 8 | // import std.experimental.logger; 9 | 10 | import hunt.logging; 11 | // import hunt.util.worker; 12 | 13 | 14 | // class TestTask : Task { 15 | 16 | // this(size_t id) { 17 | // this.id = id; 18 | // } 19 | 20 | 21 | // override void doExecute() { 22 | // infof("Task %d is running.", id); 23 | // Thread.sleep(1.seconds); 24 | // infof("Task %d is done.", id); 25 | // } 26 | // } 27 | 28 | 29 | // void main1(string[] args) { 30 | 31 | // MemoryTaskQueue memoryQueue = new MemoryTaskQueue(); 32 | // Worker worker = new Worker(memoryQueue); 33 | 34 | // // scope(exit) { 35 | // // worker.stop(); 36 | // // } 37 | 38 | // worker.run(); 39 | 40 | // foreach(size_t index; 0.. 10) { 41 | // tracef("push task %d", index); 42 | // memoryQueue.push(new TestTask(index)); 43 | // Thread.sleep(500.msecs); 44 | // } 45 | 46 | // warning("Press any to exit"); 47 | // // getchar(); 48 | // worker.stop(); 49 | // } 50 | 51 | 52 | 53 | void main() { 54 | // testThread(); 55 | testTaskPool(); 56 | } 57 | 58 | void testTaskPool() { 59 | 60 | // enum Total = 15; // It's Ok when Total <= 15 61 | enum Total = 16; // It's blocked by Thread.sleep(dur) when Total >= 16; 62 | 63 | for(size_t group = 0; group %d ...", group, index); 111 | 112 | try { 113 | useTaskPool(); // bug 114 | // useThread(); // ok 115 | } catch(Exception ex) { 116 | infof("group: %d", group); 117 | warning(ex); 118 | } 119 | 120 | Duration dur = 10.seconds; 121 | infof("Sleeping %s", dur); 122 | Thread.sleep(dur); 123 | infof("awake now"); 124 | 125 | tracef("testing done: %d => %d", group, index); 126 | } 127 | } 128 | 129 | warning("press any key to close"); 130 | getchar(); 131 | } 132 | 133 | 134 | void testThread() { 135 | 136 | Thread[] testThreads = new Thread[20]; 137 | 138 | for(size_t group = 0; group %d ...", group, index); 142 | 143 | try { 144 | useTaskPool(); // OK 145 | } catch(Exception ex) { 146 | infof("group: %d", group); 147 | warning(ex); 148 | } 149 | 150 | Thread.sleep(10.seconds); 151 | 152 | tracef("testing done: %d => %d", group, index); 153 | } 154 | }); 155 | 156 | th.start(); 157 | 158 | testThreads[group] = th; 159 | } 160 | 161 | warning("press any key to close"); 162 | 163 | for(size_t group = 0; group 0) { 63 | alias ActionN = void delegate(T); 64 | } 65 | 66 | /** 67 | * Represents a function. 68 | */ 69 | alias Func(R) = R delegate(); 70 | 71 | alias Func(T, R) = R delegate(T); 72 | alias Func(T1, T2, R) = R delegate(T1, T2); 73 | alias Func(T1, T2, T3, R) = R delegate(T1, T2, T3); 74 | alias Func(T1, T2, T3, T4, R) = R delegate(T1, T2, T3, T4); 75 | alias Func(T1, T2, T3, T4, T5, R) = R delegate(T1, T2, T3, T4, T5); 76 | alias Func(T1, T2, T3, T4, T5, T6, R) = R delegate(T1, T2, T3, T4, T5, T6); 77 | 78 | /** 79 | * Represents a function with one argument. 80 | */ 81 | alias Func1(T1, R) = R delegate(T1 t1); 82 | 83 | /** 84 | * Represents a function with two arguments. 85 | */ 86 | alias Func2(T1, T2, R) = R delegate(T1 t1, T2 t2); 87 | 88 | /** 89 | * Represents a function with three arguments. 90 | */ 91 | alias Func3(T1, T2, T3, R) = R delegate(T1 t1, T2 t2, T3 t3); 92 | 93 | /** 94 | * Represents a function with four arguments. 95 | */ 96 | alias Func4(T1, T2, T3, T4, R) = R delegate(T1 t1, T2 t2, T3 t3, T4 t4); 97 | 98 | /** 99 | * Represents a function with five arguments. 100 | */ 101 | alias Func5(T1, T2, T3, T4, T5, R) = R delegate(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5); 102 | 103 | /** 104 | * Represents a function with six arguments. 105 | */ 106 | alias Func6(T1, T2, T3, T4, T5, T6, R) = R delegate(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6); 107 | 108 | // alias FuncN(T, R) = R delegate(T[] args...); 109 | 110 | 111 | // template Function(R, T...) { 112 | // alias Function = R delegate(T); 113 | // } 114 | 115 | /** 116 | */ 117 | class EventArgs { 118 | 119 | } 120 | 121 | 122 | alias EventHandler = ActionN!(Object, EventArgs); 123 | alias SimpleEventHandler = Action; 124 | alias SimpleActionHandler = ActionN!(Object); 125 | 126 | /** 127 | * Represents an operation that accepts a single input argument and returns no 128 | * result. Unlike most other functional interfaces, {@code Consumer} is expected 129 | * to operate via side-effects. 130 | * 131 | */ 132 | alias Consumer = Action1; 133 | // alias Consumer(T) = void delegate(T t); 134 | 135 | /** 136 | */ 137 | alias Function = Func1; 138 | // alias Function(T, U) = U delegate(T); 139 | 140 | /** 141 | * Represents a supplier of results. 142 | * 143 | *

There is no requirement that a new or distinct result be returned each 144 | * time the supplier is invoked. 145 | * 146 | */ 147 | alias Supplier = Func; 148 | // alias Supplier(T) = T delegate(); 149 | 150 | /** 151 | */ 152 | alias Predicate(T) = bool delegate(T t); 153 | 154 | /** 155 | * Represents an operation that accepts two input arguments and returns no 156 | * result. This is the two-arity specialization of {@link Consumer}. 157 | * Unlike most other functional interfaces, {@code BiConsumer} is expected 158 | * to operate via side-effects. 159 | * 160 | *

This is a functional interface 161 | * whose functional method is {@link #accept(Object, Object)}. 162 | * 163 | * @param the type of the first argument to the operation 164 | * @param the type of the second argument to the operation 165 | * 166 | * @see Consumer 167 | */ 168 | alias BiConsumer = Action2; 169 | // alias BiConsumer(T, U) = void delegate(T t, U u); 170 | 171 | 172 | 173 | /** 174 | * Represents a function that accepts two arguments and produces a result. 175 | * This is the two-arity specialization of {@link Function}. 176 | * 177 | *

This is a functional interface 178 | * whose functional method is {@link #apply(Object, Object)}. 179 | * 180 | * @param the type of the first argument to the function 181 | * @param the type of the second argument to the function 182 | * @param the type of the result of the function 183 | * 184 | * @see Function 185 | */ 186 | alias BiFunction = Func2; 187 | // alias BiFunction(T, U, R) = R delegate(T t, U u); 188 | 189 | 190 | 191 | size_t hashCode(T)(T[] a...) { 192 | if (a is null) 193 | return 0; 194 | 195 | size_t result = 1; 196 | 197 | foreach (T element ; a) 198 | result = 31 * result + (element == T.init ? 0 : hashOf(element)); 199 | 200 | return result; 201 | } 202 | 203 | 204 | bool hasKey(K, V)(V[K] arr, K key) { 205 | auto v = key in arr; 206 | return v !is null; 207 | } 208 | 209 | unittest { 210 | string[int] arr; 211 | arr[1001] = "1001"; 212 | arr[1002] = "1002"; 213 | 214 | assert(arr.hasKey(1001)); 215 | assert(!arr.hasKey(1003)); 216 | } -------------------------------------------------------------------------------- /source/hunt/event/EventLoop.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A refined core library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.event.EventLoop; 13 | 14 | import hunt.event.selector; 15 | import hunt.io.channel.Common; 16 | import hunt.logging; 17 | import hunt.util.worker; 18 | 19 | import core.thread; 20 | import std.parallelism; 21 | import std.random; 22 | 23 | import core.atomic; 24 | 25 | /** 26 | * 27 | */ 28 | final class EventLoop : AbstractSelector { 29 | 30 | private static shared int idCounter = 0; 31 | 32 | this() { 33 | int id = atomicOp!("+=")(idCounter, 1); 34 | super(id-1, 1); 35 | } 36 | 37 | this(Worker worker) { 38 | int id = atomicOp!("+=")(idCounter, 1); 39 | super(id-1, 1, worker); 40 | } 41 | 42 | this(size_t id, size_t divider, Worker worker = null) { 43 | super(id, divider, worker); 44 | } 45 | 46 | override void stop() { 47 | if(isStopping()) { 48 | version (HUNT_IO_DEBUG) 49 | warningf("The event loop %d is stopping.", getId()); 50 | return; 51 | } 52 | 53 | if(isSelfThread()) { 54 | version (HUNT_IO_DEBUG) infof("Try to stop the event loop %d in another thread", getId()); 55 | auto stopTask = task(&stop); 56 | std.parallelism.taskPool.put(stopTask); 57 | } else { 58 | version (HUNT_IO_DEBUG) 59 | warningf("Stopping event loop %d...", getId()); 60 | super.stop(); 61 | } 62 | } 63 | 64 | } -------------------------------------------------------------------------------- /source/hunt/event/EventLoopGroup.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A refined core library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.event.EventLoopGroup; 13 | 14 | import hunt.event.EventLoop; 15 | import hunt.logging; 16 | import hunt.system.Memory; 17 | import hunt.util.Lifecycle; 18 | import hunt.util.worker; 19 | 20 | import core.atomic; 21 | 22 | /** 23 | * 24 | */ 25 | class EventLoopGroup : Lifecycle { 26 | private TaskQueue _pool; 27 | private Worker _worker; 28 | 29 | this(size_t ioThreadSize = (totalCPUs - 1), size_t workerThreadSize = 0) { 30 | size_t _size = ioThreadSize > 0 ? ioThreadSize : 1; 31 | 32 | version(HUNT_DEBUG) infof("ioThreadSize: %d, workerThreadSize: %d", ioThreadSize, workerThreadSize); 33 | 34 | _eventLoops = new EventLoop[_size]; 35 | 36 | if(workerThreadSize > 0) { 37 | _pool = new MemoryTaskQueue(); 38 | _worker = new Worker(_pool, workerThreadSize); 39 | _worker.run(); 40 | } 41 | 42 | foreach (i; 0 .. _size) { 43 | _eventLoops[i] = new EventLoop(i, _size, _worker); 44 | } 45 | } 46 | 47 | void start() { 48 | start(-1); 49 | } 50 | 51 | Worker worker() { 52 | return _worker; 53 | } 54 | 55 | /** 56 | timeout: in millisecond 57 | */ 58 | void start(long timeout) { 59 | if (cas(&_isRunning, false, true)) { 60 | foreach (EventLoop pool; _eventLoops) { 61 | pool.runAsync(timeout); 62 | } 63 | } 64 | } 65 | 66 | void stop() { 67 | if (!cas(&_isRunning, true, false)) 68 | return; 69 | 70 | if(_worker !is null) { 71 | _worker.stop(); 72 | } 73 | 74 | version (HUNT_IO_DEBUG) 75 | trace("stopping EventLoopGroup..."); 76 | foreach (EventLoop pool; _eventLoops) { 77 | pool.stop(); 78 | } 79 | 80 | version (HUNT_IO_DEBUG) 81 | trace("EventLoopGroup stopped."); 82 | } 83 | 84 | bool isRunning() { 85 | return _isRunning; 86 | } 87 | 88 | bool isReady() { 89 | 90 | foreach (EventLoop pool; _eventLoops) { 91 | if(!pool.isReady()) return false; 92 | } 93 | 94 | return true; 95 | } 96 | 97 | @property size_t size() { 98 | return _eventLoops.length; 99 | } 100 | 101 | EventLoop nextLoop(size_t factor) { 102 | return _eventLoops[factor % _eventLoops.length]; 103 | } 104 | 105 | EventLoop opIndex(size_t index) { 106 | auto i = index % _eventLoops.length; 107 | return _eventLoops[i]; 108 | } 109 | 110 | int opApply(scope int delegate(EventLoop) dg) { 111 | int ret = 0; 112 | foreach (pool; _eventLoops) { 113 | ret = dg(pool); 114 | if (ret) 115 | break; 116 | } 117 | return ret; 118 | } 119 | 120 | private: 121 | shared int _loopIndex; 122 | shared bool _isRunning; 123 | EventLoop[] _eventLoops; 124 | } 125 | -------------------------------------------------------------------------------- /source/hunt/event/package.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A refined core library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.event; 13 | 14 | public import hunt.event.EventLoop; 15 | public import hunt.event.EventLoopGroup; 16 | public import hunt.event.selector; 17 | public import hunt.io.channel; -------------------------------------------------------------------------------- /source/hunt/event/selector/package.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A refined core library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.event.selector; 13 | 14 | public import hunt.event.selector.Selector; 15 | 16 | version (HAVE_EPOLL) { 17 | public import hunt.event.selector.Epoll; 18 | } else version (HAVE_KQUEUE) { 19 | public import hunt.event.selector.Kqueue; 20 | 21 | } else version (HAVE_IOCP) { 22 | public import hunt.event.selector.IOCP; 23 | } else { 24 | static assert(false, "unsupported platform"); 25 | } 26 | -------------------------------------------------------------------------------- /source/hunt/event/timer/Epoll.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A refined core library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.event.timer.Epoll; 13 | 14 | // dfmt off 15 | version (HAVE_EPOLL) : 16 | // dfmt on 17 | 18 | import hunt.event.selector.Selector; 19 | import hunt.event.timer.Common; 20 | import hunt.Functions; 21 | import hunt.io.channel.Common; 22 | 23 | import core.sys.posix.unistd; 24 | import core.sys.posix.time : itimerspec, CLOCK_MONOTONIC; 25 | 26 | import core.time; 27 | import std.socket; 28 | 29 | /** 30 | */ 31 | abstract class AbstractTimer : TimerChannelBase { 32 | this(Selector loop) { 33 | super(loop); 34 | init(); 35 | } 36 | 37 | private void init() { 38 | setFlag(ChannelFlag.Read, true); 39 | _readBuffer = new UintObject(); 40 | this.handle = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); 41 | // setTimer(); 42 | } 43 | 44 | ~this() @nogc { 45 | // close(); 46 | } 47 | 48 | private bool setTimer() { 49 | itimerspec its; 50 | ulong sec, nsec; 51 | sec = time() / 1000; 52 | nsec = (time() % 1000) * 1_000_000; 53 | 54 | its.it_value.tv_sec = cast(typeof(its.it_value.tv_sec)) sec; 55 | its.it_value.tv_nsec = cast(typeof(its.it_value.tv_nsec)) nsec; 56 | its.it_interval.tv_sec = its.it_value.tv_sec; 57 | its.it_interval.tv_nsec = its.it_value.tv_nsec; 58 | const int err = timerfd_settime(this.handle, 0, &its, null); 59 | if (err == -1) { 60 | return false; 61 | } 62 | return true; 63 | } 64 | 65 | bool readTimer(scope SimpleActionHandler read) { 66 | this.clearError(); 67 | uint value; 68 | core.sys.posix.unistd.read(this.handle, &value, 8); 69 | this._readBuffer.data = value; 70 | if (read) 71 | read(this._readBuffer); 72 | return false; 73 | } 74 | 75 | override void start(bool immediately = false, bool once = false) { 76 | setTimer(); 77 | super.start(); 78 | } 79 | 80 | UintObject _readBuffer; 81 | } 82 | 83 | /** 84 | C APIs for timerfd 85 | */ 86 | enum TFD_TIMER_ABSTIME = 1 << 0; 87 | enum TFD_CLOEXEC = 0x80000; 88 | enum TFD_NONBLOCK = 0x800; 89 | 90 | extern (C) { 91 | socket_t timerfd_create(int clockid, int flags) nothrow; 92 | int timerfd_settime(int fd, int flags, const itimerspec* new_value, itimerspec* old_value) nothrow; 93 | int timerfd_gettime(int fd, itimerspec* curr_value) nothrow; 94 | } 95 | -------------------------------------------------------------------------------- /source/hunt/event/timer/IOCP.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A refined core library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.event.timer.IOCP; 13 | 14 | // dfmt off 15 | version (HAVE_IOCP) : 16 | // dfmt on 17 | 18 | import hunt.event.selector.Selector; 19 | import hunt.event.timer.Common; 20 | import hunt.Functions; 21 | import hunt.io.channel.Common; 22 | 23 | import core.time; 24 | 25 | /** 26 | */ 27 | class AbstractTimer : TimerChannelBase { 28 | this(Selector loop) { 29 | super(loop); 30 | setFlag(ChannelFlag.Read, true); 31 | _timer = new HuntWheelTimer(); 32 | _timer.timeout = &onTimerTimeout; 33 | _readBuffer = new UintObject(); 34 | } 35 | 36 | bool readTimer(scope SimpleActionHandler read) { 37 | this.clearError(); 38 | this._readBuffer.data = 1; 39 | if (read) 40 | read(this._readBuffer); 41 | return false; 42 | } 43 | 44 | private void onTimerTimeout(Object) { 45 | _timer.rest(wheelSize); 46 | this.onRead(); 47 | } 48 | 49 | override void stop() { 50 | _timer.stop(); 51 | super.stop(); 52 | } 53 | 54 | bool setTimerOut() { 55 | if (_interval > 0) { 56 | _interval = _interval > 20 ? _interval : 20; 57 | auto size = _interval / CustomTimerMinTimeOut; 58 | const auto superfluous = _interval % CustomTimerMinTimeOut; 59 | size += superfluous > CustomTimer_Next_TimeOut ? 1 : 0; 60 | size = size > 0 ? size : 1; 61 | _wheelSize = cast(uint) size; 62 | _circle = _wheelSize / CustomTimerWheelSize; 63 | return true; 64 | } 65 | return false; 66 | } 67 | 68 | @property HuntWheelTimer timer() { 69 | return _timer; 70 | } 71 | 72 | UintObject _readBuffer; 73 | 74 | private HuntWheelTimer _timer; 75 | } 76 | -------------------------------------------------------------------------------- /source/hunt/event/timer/Kqueue.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A refined core library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.event.timer.Kqueue; 13 | 14 | // dfmt off 15 | version (HAVE_KQUEUE) : 16 | // dfmt on 17 | 18 | import hunt.event.selector.Selector; 19 | import hunt.Functions; 20 | import hunt.event.timer.Common; 21 | import hunt.io.channel; 22 | 23 | import core.sys.posix.time; 24 | import std.socket; 25 | 26 | /** 27 | */ 28 | class AbstractTimer : TimerChannelBase { 29 | this(Selector loop) { 30 | super(loop); 31 | setFlag(ChannelFlag.Read, true); 32 | _sock = new Socket(AddressFamily.UNIX, SocketType.STREAM); 33 | this.handle = _sock.handle; 34 | _readBuffer = new UintObject(); 35 | } 36 | 37 | ~this() @nogc { 38 | // close(); 39 | } 40 | 41 | bool readTimer(scope SimpleActionHandler read) { 42 | this.clearError(); 43 | this._readBuffer.data = 1; 44 | if (read) 45 | read(this._readBuffer); 46 | return false; 47 | } 48 | 49 | UintObject _readBuffer; 50 | Socket _sock; 51 | } 52 | -------------------------------------------------------------------------------- /source/hunt/event/timer/package.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A refined core library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.event.timer; 13 | 14 | public import hunt.event.timer.Common; 15 | 16 | version (HAVE_EPOLL) { 17 | public import hunt.event.timer.Epoll; 18 | } else version (HAVE_KQUEUE) { 19 | public import hunt.event.timer.Kqueue; 20 | } else version (HAVE_IOCP) { 21 | public import hunt.event.timer.IOCP; 22 | } 23 | -------------------------------------------------------------------------------- /source/hunt/io/IoError.d: -------------------------------------------------------------------------------- 1 | module hunt.io.IoError; 2 | 3 | enum ErrorCode 4 | { 5 | NOTFOUND, //An entity was not found, often a file. 6 | PERMISSIONENIED, //The operation lacked the necessary privileges to complete. 7 | CONNECTIONEFUSED, //The connection was refused by the remote server. 8 | CONNECTIONEESET, //The connection was reset by the remote server. 9 | CONNECTIONABORTED, //The connection was aborted (terminated) by the remote server. 10 | NOTCONNECTED, //The network operation failed because it was not connected yet. 11 | ADDRINUSE, //A socket address could not be bound because the address is already in use elsewhere. 12 | ADDRNOTAVAILABLE, //A nonexistent interface was requested or the requested address was not local. 13 | BROKENPIPE, //The operation failed because a pipe was closed. 14 | ALREADYEXISTS, //An entity already exists, often a file. 15 | WOULDBLOCK, //The operation needs to block to complete, but the blocking operation was requested to not occur. 16 | INVALIDINPUT, //A parameter was incorrect. 17 | INVALIDDATA, //Data not valid for the operation were encountered. 18 | TIMEDOUT, //The I/O operation's timeout expired, causing it to be canceled. 19 | WRITEZERO, //An error returned when an operation could not be completed because a call to write returned Ok(0). 20 | INTERRUPTED, //This operation was interrupted. 21 | OTHER, //Any I/O error not part of this list. 22 | UNEXPECTEDEOF //An error returned when an operation could not be completed because an "end of file" was reached prematurely. 23 | } 24 | 25 | class IoError 26 | { 27 | private 28 | { 29 | ErrorCode _errorCode; 30 | string _errorMsg; 31 | } 32 | 33 | ErrorCode errorCode() 34 | { 35 | return _errorCode; 36 | } 37 | 38 | string errorMsg() 39 | { 40 | return _errorMsg; 41 | } 42 | 43 | this(ErrorCode code , string msg) 44 | { 45 | this._errorCode = code; 46 | this._errorMsg = msg; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /source/hunt/io/TcpStreamOptions.d: -------------------------------------------------------------------------------- 1 | module hunt.io.TcpStreamOptions; 2 | 3 | import core.time; 4 | 5 | /** 6 | * 7 | */ 8 | class TcpStreamOptions { 9 | 10 | // http://www.tldp.org/HOWTO/TCP-Keepalive-HOWTO/usingkeepalive.html 11 | /// the interval between the last data packet sent (simple ACKs are not considered data) and the first keepalive probe; 12 | /// after the connection is marked to need keepalive, this counter is not used any further 13 | int keepaliveTime = 7200; // in seconds 14 | 15 | /// the interval between subsequential keepalive probes, regardless of what the connection has exchanged in the meantime 16 | int keepaliveInterval = 75; // in seconds 17 | 18 | /// the number of unacknowledged probes to send before considering the connection dead and notifying the application layer 19 | int keepaliveProbes = 9; // times 20 | 21 | bool isKeepalive = false; 22 | 23 | size_t bufferSize = 1024 * 8; 24 | 25 | int retryTimes = 5; 26 | Duration retryInterval = 2.seconds; 27 | 28 | this() { 29 | 30 | } 31 | 32 | static TcpStreamOptions create() { 33 | TcpStreamOptions option = new TcpStreamOptions(); 34 | option.isKeepalive = true; 35 | option.keepaliveTime = 15; 36 | option.keepaliveInterval = 3; 37 | option.keepaliveProbes = 5; 38 | option.bufferSize = 1024 * 8; 39 | return option; 40 | } 41 | } -------------------------------------------------------------------------------- /source/hunt/io/UdpSocket.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A refined core library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.io.UdpSocket; 13 | 14 | import hunt.event; 15 | import hunt.logging; 16 | 17 | import std.socket; 18 | import std.exception; 19 | 20 | /** 21 | */ 22 | class UdpSocket : AbstractDatagramSocket { 23 | 24 | private UdpDataHandler _receivedHandler; 25 | 26 | this(EventLoop loop, AddressFamily amily = AddressFamily.INET) { 27 | super(loop, amily); 28 | } 29 | 30 | UdpSocket enableBroadcast(bool flag) { 31 | this.socket.setOption(SocketOptionLevel.SOCKET, SocketOption.BROADCAST, flag); 32 | return this; 33 | } 34 | 35 | UdpSocket onReceived(UdpDataHandler handler) { 36 | _receivedHandler = handler; 37 | return this; 38 | } 39 | 40 | ptrdiff_t sendTo(const(void)[] buf, Address to) { 41 | return this.socket.sendTo(buf, to); 42 | } 43 | 44 | ptrdiff_t sendTo(const(void)[] buf) { 45 | return this.socket.sendTo(buf); 46 | } 47 | 48 | ptrdiff_t sendTo(const(void)[] buf, SocketFlags flags, Address to) { 49 | return this.socket.sendTo(buf, flags, to); 50 | } 51 | 52 | UdpSocket bind(string ip, ushort port) { 53 | super.bind(parseAddress(ip, port)); 54 | return this; 55 | } 56 | 57 | UdpSocket bind(Address address) { 58 | super.bind(address); 59 | return this; 60 | } 61 | 62 | UdpSocket connect(Address addr) { 63 | this.socket.connect(addr); 64 | return this; 65 | } 66 | 67 | override void start() { 68 | if (!_binded) { 69 | socket.bind(_bindAddress); 70 | _binded = true; 71 | } 72 | 73 | _inLoop.register(this); 74 | _isRegistered = true; 75 | version (HAVE_IOCP) 76 | doRead(); 77 | } 78 | 79 | protected override void onRead() nothrow { 80 | catchAndLogException(() { 81 | bool canRead = true; 82 | while (canRead && _isRegistered) { 83 | version (HUNT_IO_DEBUG) 84 | trace("reading data..."); 85 | canRead = tryRead((Object obj) nothrow{ 86 | collectException(() { 87 | UdpDataObject data = cast(UdpDataObject) obj; 88 | if (data !is null) { 89 | _receivedHandler(data.data, data.addr); 90 | } 91 | }()); 92 | }); 93 | 94 | if (this.isError) { 95 | canRead = false; 96 | this.close(); 97 | error("UDP socket error: ", this.errorMessage); 98 | } 99 | } 100 | }()); 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /source/hunt/io/channel/AbstractChannel.d: -------------------------------------------------------------------------------- 1 | module hunt.io.channel.AbstractChannel; 2 | 3 | import hunt.event.selector.Selector; 4 | import hunt.io.channel.Common; 5 | import hunt.io.IoError; 6 | import hunt.logging; 7 | import hunt.util.worker; 8 | 9 | import core.atomic; 10 | import std.bitmanip; 11 | import std.socket : socket_t; 12 | 13 | 14 | /** 15 | * 16 | */ 17 | abstract class AbstractChannel : Channel { 18 | socket_t handle = socket_t.init; 19 | ErrorEventHandler errorHandler; 20 | 21 | Worker taskWorker = null; 22 | 23 | protected bool _isRegistered = false; 24 | private shared bool _isClosing = false; 25 | protected shared bool _isClosed = false; 26 | 27 | this(Selector loop, ChannelType type) { 28 | this._inLoop = loop; 29 | _type = type; 30 | _flags = BitArray([false, false, false, false, false, false, false, 31 | false, false, false, false, false, false, false, false, false]); 32 | } 33 | 34 | /** 35 | * 36 | */ 37 | bool isRegistered() { 38 | return _isRegistered; 39 | } 40 | 41 | /** 42 | * 43 | */ 44 | bool isClosing() { 45 | return _isClosing; 46 | } 47 | 48 | /** 49 | * 50 | */ 51 | bool isClosed() { 52 | return _isClosed; 53 | } 54 | 55 | /** 56 | * 57 | */ 58 | void close() { 59 | if (!_isClosed && cas(&_isClosing, false, true) ) { 60 | version (HUNT_IO_DEBUG_MORE) 61 | tracef("channel[fd=%d] closing...", this.handle); 62 | 63 | // closing 64 | doClose(); // close 65 | _isClosed = true; 66 | 67 | // closed 68 | onClose(); 69 | _isClosing = false; 70 | version (HUNT_IO_DEBUG) 71 | tracef("channel[fd=%d] closed", this.handle); 72 | 73 | } else { 74 | version (HUNT_IO_DEBUG) { 75 | warningf("The channel[fd=%d] has already been closed (%s) or closing (%s)", 76 | this.handle, _isClosed, _isClosing); 77 | } 78 | } 79 | } 80 | 81 | protected void doClose() { 82 | 83 | } 84 | 85 | void onClose() { 86 | version (HUNT_IO_DEBUG) 87 | tracef("onClose [fd=%d]...", this.handle); 88 | _isRegistered = false; 89 | _isClosed = true; 90 | clear(); 91 | 92 | _inLoop.deregister(this); 93 | version (HUNT_IO_DEBUG_MORE) 94 | tracef("onClose done [fd=%d]...", this.handle); 95 | } 96 | 97 | protected void errorOccurred(ErrorCode code, string msg) { 98 | debug warningf("isRegistered: %s, _isClosing: %s, isClosed: %s, code: %s, msg=%s", 99 | _isRegistered, _isClosing, _isClosed, code, msg); 100 | 101 | if (errorHandler !is null) { 102 | errorHandler(new IoError(code, msg)); 103 | } 104 | } 105 | 106 | void onRead() { 107 | assert(false, "not implemented"); 108 | } 109 | 110 | void onWrite() { 111 | assert(false, "not implemented"); 112 | } 113 | 114 | final bool hasFlag(ChannelFlag index) { 115 | return _flags[index]; 116 | } 117 | 118 | @property ChannelType type() { 119 | return _type; 120 | } 121 | 122 | @property Selector eventLoop() { 123 | return _inLoop; 124 | } 125 | 126 | void setNext(AbstractChannel next) { 127 | if (next is this) 128 | return; // Can't set to self 129 | next._next = _next; 130 | next._priv = this; 131 | if (_next) 132 | _next._priv = next; 133 | this._next = next; 134 | } 135 | 136 | void clear() { 137 | if (_priv) 138 | _priv._next = _next; 139 | if (_next) 140 | _next._priv = _priv; 141 | _next = null; 142 | _priv = null; 143 | } 144 | 145 | mixin OverrideErro; 146 | 147 | protected: 148 | final void setFlag(ChannelFlag index, bool enable) { 149 | _flags[index] = enable; 150 | } 151 | 152 | Selector _inLoop; 153 | 154 | private: 155 | BitArray _flags; 156 | ChannelType _type; 157 | 158 | AbstractChannel _priv; 159 | AbstractChannel _next; 160 | } 161 | 162 | 163 | 164 | /** 165 | https://stackoverflow.com/questions/40361869/how-to-wake-up-epoll-wait-before-any-event-happened 166 | */ 167 | class EventChannel : AbstractChannel { 168 | this(Selector loop) { 169 | super(loop, ChannelType.Event); 170 | } 171 | 172 | abstract void trigger(); 173 | // override void close() { 174 | // if(_isClosing) 175 | // return; 176 | // _isClosing = true; 177 | // version (HUNT_DEBUG) tracef("closing [fd=%d]...", this.handle); 178 | 179 | // if(isBusy) { 180 | // import std.parallelism; 181 | // version (HUNT_DEBUG) warning("Close operation delayed"); 182 | // auto theTask = task(() { 183 | // while(isBusy) { 184 | // version (HUNT_DEBUG) infof("waitting for idle [fd=%d]...", this.handle); 185 | // // Thread.sleep(20.msecs); 186 | // } 187 | // super.close(); 188 | // }); 189 | // taskPool.put(theTask); 190 | // } else { 191 | // super.close(); 192 | // } 193 | // } 194 | } 195 | 196 | mixin template OverrideErro() { 197 | bool isError() { 198 | return _error; 199 | } 200 | 201 | deprecated("Using errorMessage instead.") 202 | alias erroString = errorMessage; 203 | 204 | string errorMessage() { 205 | return _errorMessage; 206 | } 207 | 208 | void clearError() { 209 | _error = false; 210 | _errorMessage = ""; 211 | } 212 | 213 | bool _error = false; 214 | string _errorMessage; 215 | } 216 | -------------------------------------------------------------------------------- /source/hunt/io/channel/AbstractSocketChannel.d: -------------------------------------------------------------------------------- 1 | module hunt.io.channel.AbstractSocketChannel; 2 | 3 | import hunt.event.selector.Selector; 4 | import hunt.io.channel.AbstractChannel; 5 | import hunt.io.channel.Common; 6 | import hunt.logging; 7 | 8 | import core.time; 9 | import std.functional; 10 | import std.socket; 11 | import core.stdc.stdint; 12 | 13 | /** 14 | * 15 | */ 16 | abstract class AbstractSocketChannel : AbstractChannel { 17 | 18 | protected shared bool _isWritting = false; // keep a data write operation atomic 19 | 20 | this(Selector loop, ChannelType type) { 21 | super(loop, type); 22 | } 23 | 24 | // Busy with reading or writting 25 | protected bool isBusy() { 26 | return false; 27 | } 28 | 29 | protected @property void socket(Socket s) { 30 | this.handle = s.handle(); 31 | version (Posix) { 32 | s.blocking = false; 33 | } 34 | _socket = s; 35 | version (HUNT_DEBUG_MORE) 36 | infof("new socket: fd=%d", this.handle); 37 | } 38 | 39 | protected @property Socket socket() { 40 | return _socket; 41 | } 42 | 43 | protected Socket _socket; 44 | 45 | override void close() { 46 | // if (_isClosing) { 47 | // // debug warningf("already closed [fd=%d]", this.handle); 48 | // return; 49 | // } 50 | // _isClosing = true; 51 | version (HUNT_IO_MORE) 52 | tracef("socket channel closing [fd=%d]...", this.handle); 53 | version (HAVE_IOCP) 54 | { 55 | super.close(); 56 | } else 57 | { 58 | if (isBusy()) { 59 | import std.parallelism; 60 | 61 | version (HUNT_DEBUG) 62 | warning("Close operation delayed"); 63 | auto theTask = task(() { 64 | super.close(); 65 | while (isBusy()) { 66 | version (HUNT_DEBUG) 67 | infof("waitting for idle [fd=%d]...", this.handle); 68 | // Thread.sleep(20.msecs); 69 | } 70 | }); 71 | taskPool.put(theTask); 72 | } else { 73 | super.close(); 74 | } 75 | } 76 | } 77 | 78 | /// Get a socket option. 79 | /// Returns: The number of bytes written to $(D result). 80 | /// returns the length, in bytes, of the actual result - very different from getsockopt() 81 | pragma(inline) final int getOption(SocketOptionLevel level, SocketOption option, void[] result) @trusted { 82 | return this._socket.getOption(level, option, result); 83 | } 84 | 85 | /// Common case of getting integer and boolean options. 86 | pragma(inline) final int getOption(SocketOptionLevel level, 87 | SocketOption option, ref int32_t result) @trusted { 88 | return this._socket.getOption(level, option, result); 89 | } 90 | 91 | /// Get the linger option. 92 | pragma(inline) final int getOption(SocketOptionLevel level, SocketOption option, 93 | ref Linger result) @trusted { 94 | return this._socket.getOption(level, option, result); 95 | } 96 | 97 | /// Get a timeout (duration) option. 98 | pragma(inline) final void getOption(SocketOptionLevel level, 99 | SocketOption option, ref Duration result) @trusted { 100 | this._socket.getOption(level, option, result); 101 | } 102 | 103 | /// Set a socket option. 104 | pragma(inline) final void setOption(SocketOptionLevel level, SocketOption option, void[] value) @trusted { 105 | this._socket.setOption(forward!(level, option, value)); 106 | } 107 | 108 | /// Common case for setting integer and boolean options. 109 | pragma(inline) final void setOption(SocketOptionLevel level, SocketOption option, int32_t value) @trusted { 110 | this._socket.setOption(forward!(level, option, value)); 111 | } 112 | 113 | /// Set the linger option. 114 | pragma(inline) final void setOption(SocketOptionLevel level, SocketOption option, Linger value) @trusted { 115 | this._socket.setOption(forward!(level, option, value)); 116 | } 117 | 118 | pragma(inline) final void setOption(SocketOptionLevel level, SocketOption option, Duration value) @trusted { 119 | this._socket.setOption(forward!(level, option, value)); 120 | } 121 | 122 | final @property @trusted Address remoteAddress() { 123 | return _remoteAddress; 124 | } 125 | 126 | protected Address _remoteAddress; 127 | 128 | final @property @trusted Address localAddress() { 129 | return _localAddress; 130 | } 131 | 132 | protected Address _localAddress; 133 | 134 | version (HAVE_IOCP) { 135 | void setRead(size_t bytes) { 136 | readLen = bytes; 137 | } 138 | 139 | protected size_t readLen; 140 | } 141 | 142 | void start(); 143 | 144 | void onWriteDone() { 145 | assert(false, "unimplemented"); 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /source/hunt/io/channel/ChannelTask.d: -------------------------------------------------------------------------------- 1 | module hunt.io.channel.ChannelTask; 2 | 3 | import hunt.event.selector.Selector; 4 | import hunt.Functions; 5 | import hunt.io.BufferUtils; 6 | import hunt.io.ByteBuffer; 7 | import hunt.io.channel.AbstractSocketChannel; 8 | import hunt.io.channel.Common; 9 | import hunt.io.IoError; 10 | // import hunt.io.SimpleQueue; 11 | import hunt.logging; 12 | import hunt.system.Error; 13 | import hunt.util.queue; 14 | import hunt.util.worker; 15 | 16 | 17 | import std.format; 18 | import std.socket; 19 | 20 | import core.atomic; 21 | 22 | /** 23 | * 24 | */ 25 | class ChannelTask : Task { 26 | DataReceivedHandler dataReceivedHandler; 27 | private shared bool _isFinishing = false; 28 | private Queue!(ByteBuffer) _buffers; 29 | 30 | this() { 31 | _buffers = new SimpleQueue!(ByteBuffer); 32 | } 33 | 34 | void put(ByteBuffer buffer) { 35 | _buffers.push(buffer); 36 | } 37 | 38 | bool isFinishing () { 39 | return _isFinishing; 40 | } 41 | 42 | override protected void doExecute() { 43 | 44 | ByteBuffer buffer; 45 | DataHandleStatus handleStatus = DataHandleStatus.Pending; 46 | 47 | do { 48 | buffer = _buffers.pop(); 49 | if(buffer is null) { 50 | version(HUNT_IO_DEBUG) { 51 | warning("A null buffer poped"); 52 | } 53 | break; 54 | } 55 | 56 | version(HUNT_IO_DEBUG) { 57 | tracef("buffer: %s", buffer.toString()); 58 | } 59 | 60 | handleStatus = dataReceivedHandler(buffer); 61 | 62 | version(HUNT_IO_DEBUG) { 63 | tracef("Handle status: %s, buffer: %s", handleStatus, buffer.toString()); 64 | } 65 | 66 | _isFinishing = isTerminated(); 67 | if(!_isFinishing) { 68 | _isFinishing = handleStatus == DataHandleStatus.Done && !buffer.hasRemaining() && _buffers.isEmpty(); 69 | } 70 | 71 | if(_isFinishing) { 72 | version(HUNT_DEBUG) { 73 | if(buffer.hasRemaining() || !_buffers.isEmpty()) { 74 | warningf("The buffered data lost"); 75 | } 76 | } 77 | break; 78 | } 79 | } while(true); 80 | } 81 | } 82 | 83 | -------------------------------------------------------------------------------- /source/hunt/io/channel/Common.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A refined core library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.io.channel.Common; 13 | 14 | import hunt.io.IoError; 15 | import hunt.io.ByteBuffer; 16 | import hunt.io.SimpleQueue; 17 | import hunt.util.TaskPool; 18 | import hunt.Functions; 19 | import hunt.system.Memory; 20 | 21 | import core.atomic; 22 | import std.socket; 23 | 24 | enum DataHandleStatus { 25 | Done, 26 | Pending 27 | } 28 | 29 | 30 | alias DataReceivedHandler = DataHandleStatus delegate(ByteBuffer buffer); 31 | alias AcceptHandler = void delegate(Socket socket); 32 | alias ErrorEventHandler = Action1!(IoError); 33 | 34 | alias ConnectionHandler = void delegate(bool isSucceeded); 35 | alias UdpDataHandler = void delegate(const(ubyte)[] data, Address addr); 36 | 37 | @property TaskPool workerPool() @trusted { 38 | import std.concurrency : initOnce; 39 | 40 | __gshared TaskPool pool; 41 | return initOnce!pool({ 42 | return new TaskPool(defaultPoolThreads, true); 43 | }()); 44 | } 45 | 46 | // __gshared bool useWorkerThread = false; 47 | 48 | private shared uint _defaultPoolThreads = 0; 49 | 50 | /** 51 | These properties get and set the number of worker threads in the `TaskPool` 52 | instance returned by `taskPool`. The default value is `totalCPUs` - 1. 53 | Calling the setter after the first call to `taskPool` does not changes 54 | number of worker threads in the instance returned by `taskPool`. 55 | */ 56 | @property uint defaultPoolThreads() @trusted { 57 | const local = atomicLoad(_defaultPoolThreads); 58 | return local < uint.max ? local : totalCPUs - 1; 59 | } 60 | 61 | /// Ditto 62 | @property void defaultPoolThreads(uint newVal) @trusted { 63 | atomicStore(_defaultPoolThreads, newVal); 64 | } 65 | 66 | 67 | /** 68 | */ 69 | interface Channel { 70 | 71 | } 72 | 73 | 74 | 75 | enum ChannelType : ubyte { 76 | Accept = 0, 77 | TCP, 78 | UDP, 79 | Timer, 80 | Event, 81 | File, 82 | None 83 | } 84 | 85 | enum ChannelFlag : ushort { 86 | None = 0, 87 | Read, 88 | Write, 89 | 90 | OneShot = 8, 91 | ETMode = 16 92 | } 93 | 94 | final class UdpDataObject { 95 | Address addr; 96 | ubyte[] data; 97 | } 98 | 99 | final class BaseTypeObject(T) { 100 | T data; 101 | } 102 | 103 | 104 | 105 | // version (HUNT_IO_WORKERPOOL) { 106 | // alias WritingBufferQueue = MagedNonBlockingQueue!ByteBuffer; 107 | // } else { 108 | // alias WritingBufferQueue = SimpleQueue!ByteBuffer; 109 | // } 110 | 111 | // alias WritingBufferQueue = MagedNonBlockingQueue!ByteBuffer; 112 | alias WritingBufferQueue = SimpleQueue!ByteBuffer; 113 | // alias WritingBufferQueue = MagedBlockingQueue!ByteBuffer; 114 | 115 | /** 116 | */ 117 | Address createAddress(AddressFamily family = AddressFamily.INET, 118 | ushort port = InternetAddress.PORT_ANY) { 119 | if (family == AddressFamily.INET6) { 120 | // addr = new Internet6Address(port); // bug on windows 121 | return new Internet6Address("::", port); 122 | } else 123 | return new InternetAddress(port); 124 | } 125 | -------------------------------------------------------------------------------- /source/hunt/io/channel/iocp/AbstractDatagramSocket.d: -------------------------------------------------------------------------------- 1 | module hunt.io.channel.iocp.AbstractDatagramSocket; 2 | 3 | // dfmt off 4 | version (HAVE_IOCP) : 5 | // dfmt on 6 | 7 | // import hunt.io.ByteBuffer; 8 | // import hunt.io.BufferUtils; 9 | import hunt.event.selector.Selector; 10 | import hunt.io.channel.AbstractSocketChannel; 11 | import hunt.io.channel.Common; 12 | import hunt.io.channel.iocp.Common; 13 | import hunt.logging; 14 | import hunt.Functions; 15 | 16 | // import hunt.util.ThreadHelper; 17 | 18 | // import core.atomic; 19 | import core.sys.windows.windows; 20 | import core.sys.windows.winsock2; 21 | import core.sys.windows.mswsock; 22 | 23 | // import std.conv; 24 | // import std.exception; 25 | // import std.format; 26 | // import std.process; 27 | import std.socket; 28 | 29 | // import std.stdio; 30 | 31 | /** 32 | UDP Socket 33 | */ 34 | abstract class AbstractDatagramSocket : AbstractSocketChannel { 35 | /// Constructs a blocking IPv4 UDP Socket. 36 | this(Selector loop, AddressFamily family = AddressFamily.INET) { 37 | super(loop, ChannelType.UDP); 38 | setFlag(ChannelFlag.Read, true); 39 | // setFlag(ChannelFlag.ETMode, false); 40 | 41 | this.socket = new UdpSocket(family); 42 | _readBuffer = new UdpDataObject(); 43 | _readBuffer.data = new ubyte[4096 * 2]; 44 | 45 | if (family == AddressFamily.INET) 46 | _bindAddress = new InternetAddress(InternetAddress.PORT_ANY); 47 | else if (family == AddressFamily.INET6) 48 | _bindAddress = new Internet6Address(Internet6Address.PORT_ANY); 49 | else 50 | _bindAddress = new UnknownAddress(); 51 | } 52 | 53 | final void bind(Address addr) { 54 | if (_binded) 55 | return; 56 | _bindAddress = addr; 57 | socket.bind(_bindAddress); 58 | _binded = true; 59 | } 60 | 61 | final bool isBind() { 62 | return _binded; 63 | } 64 | 65 | Address bindAddr() { 66 | return _bindAddress; 67 | } 68 | 69 | override void start() { 70 | if (!_binded) { 71 | socket.bind(_bindAddress); 72 | _binded = true; 73 | } 74 | } 75 | 76 | // abstract void doRead(); 77 | 78 | private UdpDataObject _readBuffer; 79 | protected bool _binded = false; 80 | protected Address _bindAddress; 81 | 82 | mixin CheckIocpError; 83 | 84 | void doRead() { 85 | version (HUNT_IO_DEBUG) 86 | trace("Receiving......"); 87 | 88 | _dataReadBuffer.len = cast(uint) _readBuffer.data.length; 89 | _dataReadBuffer.buf = cast(char*) _readBuffer.data.ptr; 90 | _iocpread.channel = this; 91 | _iocpread.operation = IocpOperation.read; 92 | remoteAddrLen = cast(int) bindAddr().nameLen(); 93 | 94 | DWORD dwReceived = 0; 95 | DWORD dwFlags = 0; 96 | 97 | int nRet = WSARecvFrom(cast(SOCKET) this.handle, &_dataReadBuffer, 98 | cast(uint) 1, &dwReceived, &dwFlags, cast(SOCKADDR*)&remoteAddr, &remoteAddrLen, 99 | &_iocpread.overlapped, cast(LPWSAOVERLAPPED_COMPLETION_ROUTINE) null); 100 | checkErro(nRet, SOCKET_ERROR); 101 | } 102 | 103 | Address buildAddress() { 104 | Address tmpaddr; 105 | if (remoteAddrLen == 32) { 106 | sockaddr_in* addr = cast(sockaddr_in*)(&remoteAddr); 107 | tmpaddr = new InternetAddress(*addr); 108 | } else { 109 | sockaddr_in6* addr = cast(sockaddr_in6*)(&remoteAddr); 110 | tmpaddr = new Internet6Address(*addr); 111 | } 112 | return tmpaddr; 113 | } 114 | 115 | bool tryRead(scope SimpleActionHandler read) { 116 | this.clearError(); 117 | if (this.readLen == 0) { 118 | read(null); 119 | } else { 120 | ubyte[] data = this._readBuffer.data; 121 | this._readBuffer.data = data[0 .. this.readLen]; 122 | this._readBuffer.addr = this.buildAddress(); 123 | scope (exit) 124 | this._readBuffer.data = data; 125 | read(this._readBuffer); 126 | this._readBuffer.data = data; 127 | if (this.isRegistered) 128 | this.doRead(); 129 | } 130 | return false; 131 | } 132 | 133 | IocpContext _iocpread; 134 | WSABUF _dataReadBuffer; 135 | 136 | sockaddr remoteAddr; 137 | int remoteAddrLen; 138 | 139 | } 140 | -------------------------------------------------------------------------------- /source/hunt/io/channel/iocp/AbstractListener.d: -------------------------------------------------------------------------------- 1 | module hunt.io.channel.iocp.AbstractListener; 2 | 3 | // dfmt off 4 | version (HAVE_IOCP) : 5 | // dfmt on 6 | 7 | import hunt.event.selector.Selector; 8 | import hunt.io.channel.AbstractSocketChannel; 9 | import hunt.io.channel.Common; 10 | import hunt.io.channel.iocp.Common; 11 | import hunt.logging; 12 | import hunt.Functions; 13 | 14 | import core.sys.windows.windows; 15 | import core.sys.windows.winsock2; 16 | import core.sys.windows.mswsock; 17 | 18 | import std.socket; 19 | 20 | 21 | 22 | /** 23 | TCP Server 24 | */ 25 | abstract class AbstractListener : AbstractSocketChannel { 26 | this(Selector loop, AddressFamily family = AddressFamily.INET, size_t bufferSize = 4 * 1024) { 27 | super(loop, ChannelType.Accept); 28 | setFlag(ChannelFlag.Read, true); 29 | _buffer = new ubyte[bufferSize]; 30 | this.socket = new TcpSocket(family); 31 | 32 | loadWinsockExtension(this.handle); 33 | } 34 | 35 | mixin CheckIocpError; 36 | 37 | protected void doAccept() { 38 | _iocp.channel = this; 39 | _iocp.operation = IocpOperation.accept; 40 | _clientSocket = new Socket(this.localAddress.addressFamily, 41 | SocketType.STREAM, ProtocolType.TCP); 42 | DWORD dwBytesReceived = 0; 43 | 44 | version (HUNT_DEBUG) { 45 | tracef("client socket: acceptor=%s inner socket=%s", this.handle, 46 | _clientSocket.handle()); 47 | // info("AcceptEx@", AcceptEx); 48 | } 49 | uint sockaddrSize = cast(uint) sockaddr_storage.sizeof; 50 | // https://docs.microsoft.com/en-us/windows/desktop/api/mswsock/nf-mswsock-acceptex 51 | BOOL ret = AcceptEx(this.handle, cast(SOCKET) _clientSocket.handle, _buffer.ptr, 52 | 0, sockaddrSize + 16, sockaddrSize + 16, &dwBytesReceived, &_iocp.overlapped); 53 | version (HUNT_DEBUG) 54 | trace("AcceptEx return: ", ret); 55 | checkErro(ret, FALSE); 56 | } 57 | 58 | protected bool onAccept(scope AcceptHandler handler) { 59 | version (HUNT_DEBUG) 60 | trace("a new connection coming..."); 61 | this.clearError(); 62 | SOCKET slisten = cast(SOCKET) this.handle; 63 | SOCKET slink = cast(SOCKET) this._clientSocket.handle; 64 | // void[] value = (&slisten)[0..1]; 65 | // setsockopt(slink, SocketOptionLevel.SOCKET, 0x700B, value.ptr, 66 | // cast(uint) value.length); 67 | version (HUNT_DEBUG) 68 | tracef("slisten=%s, slink=%s", slisten, slink); 69 | setsockopt(slink, SocketOptionLevel.SOCKET, 0x700B, cast(void*)&slisten, slisten.sizeof); 70 | if (handler !is null) 71 | handler(this._clientSocket); 72 | 73 | version (HUNT_DEBUG) 74 | trace("accepting next connection..."); 75 | if (this.isRegistered) 76 | this.doAccept(); 77 | return true; 78 | } 79 | 80 | override void onClose() { 81 | 82 | // version (HUNT_DEBUG) 83 | // tracef("_isWritting=%s", _isWritting); 84 | // _isWritting = false; 85 | // assert(false, ""); 86 | // TODO: created by Administrator @ 2018-3-27 15:51:52 87 | } 88 | 89 | private IocpContext _iocp; 90 | private WSABUF _dataWriteBuffer; 91 | private ubyte[] _buffer; 92 | private Socket _clientSocket; 93 | } 94 | -------------------------------------------------------------------------------- /source/hunt/io/channel/iocp/Common.d: -------------------------------------------------------------------------------- 1 | module hunt.io.channel.iocp.Common; 2 | 3 | 4 | // dfmt off 5 | version (HAVE_IOCP) : 6 | 7 | pragma(lib, "Ws2_32"); 8 | // dfmt on 9 | 10 | import hunt.io.ByteBuffer; 11 | import hunt.io.channel.AbstractChannel; 12 | import hunt.io.channel.Common; 13 | import hunt.logging; 14 | import hunt.Functions; 15 | 16 | import core.atomic; 17 | import core.sys.windows.windows; 18 | import core.sys.windows.winsock2; 19 | import core.sys.windows.mswsock; 20 | 21 | import std.conv; 22 | import std.exception; 23 | import std.format; 24 | import std.process; 25 | import std.socket; 26 | import std.stdio; 27 | 28 | 29 | /** 30 | * 31 | */ 32 | mixin template CheckIocpError() { 33 | void checkErro(int ret, int erro = 0) { 34 | DWORD dwLastError = GetLastError(); 35 | version (HUNT_IO_DEBUG) 36 | infof("ret=%d, erro=%d, dwLastError=%d", ret, erro, dwLastError); 37 | if (ret != erro || dwLastError == 0) 38 | return; 39 | 40 | if (ERROR_IO_PENDING != dwLastError) { // ERROR_IO_PENDING 41 | import hunt.system.Error; 42 | warningf("erro=%d, dwLastError=%d", erro, dwLastError); 43 | this._error = true; 44 | this._errorMessage = getErrorMessage(dwLastError); // format("IOCP error: code=%s", dwLastError); 45 | } 46 | } 47 | } 48 | 49 | enum IocpOperation { 50 | accept, 51 | connect, 52 | read, 53 | write, 54 | event, 55 | close 56 | } 57 | 58 | struct IocpContext { 59 | OVERLAPPED overlapped; 60 | IocpOperation operation; 61 | AbstractChannel channel = null; 62 | } 63 | 64 | alias WSAOVERLAPPED = OVERLAPPED; 65 | alias LPWSAOVERLAPPED = OVERLAPPED*; 66 | 67 | __gshared static LPFN_ACCEPTEX AcceptEx; 68 | __gshared static LPFN_CONNECTEX ConnectEx; 69 | /*__gshared LPFN_DISCONNECTEX DisconnectEx; 70 | __gshared LPFN_GETACCEPTEXSOCKADDRS GetAcceptexSockAddrs; 71 | __gshared LPFN_TRANSMITFILE TransmitFile; 72 | __gshared LPFN_TRANSMITPACKETS TransmitPackets; 73 | __gshared LPFN_WSARECVMSG WSARecvMsg; 74 | __gshared LPFN_WSASENDMSG WSASendMsg;*/ 75 | 76 | shared static this() { 77 | WSADATA wsaData; 78 | int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); 79 | if (iResult != 0) { 80 | stderr.writeln("unable to load Winsock!"); 81 | } 82 | } 83 | 84 | shared static ~this() { 85 | WSACleanup(); 86 | } 87 | 88 | void loadWinsockExtension(SOCKET socket) { 89 | if (isApiLoaded) 90 | return; 91 | isApiLoaded = true; 92 | 93 | // SOCKET ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 94 | // scope (exit) 95 | // closesocket(ListenSocket); 96 | GUID guid; 97 | mixin(GET_FUNC_POINTER("WSAID_ACCEPTEX", "AcceptEx", socket.stringof)); 98 | mixin(GET_FUNC_POINTER("WSAID_CONNECTEX", "ConnectEx")); 99 | /* mixin(GET_FUNC_POINTER("WSAID_DISCONNECTEX", "DisconnectEx")); 100 | mixin(GET_FUNC_POINTER("WSAID_GETACCEPTEXSOCKADDRS", "GetAcceptexSockAddrs")); 101 | mixin(GET_FUNC_POINTER("WSAID_TRANSMITFILE", "TransmitFile")); 102 | mixin(GET_FUNC_POINTER("WSAID_TRANSMITPACKETS", "TransmitPackets")); 103 | mixin(GET_FUNC_POINTER("WSAID_WSARECVMSG", "WSARecvMsg"));*/ 104 | } 105 | 106 | private __gshared bool isApiLoaded = false; 107 | 108 | private bool GetFunctionPointer(FuncPointer)(SOCKET sock, ref FuncPointer pfn, ref GUID guid) { 109 | DWORD dwBytesReturned = 0; 110 | if (WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, guid.sizeof, 111 | &pfn, pfn.sizeof, &dwBytesReturned, null, null) == SOCKET_ERROR) { 112 | error("Get function failed with error:", GetLastError()); 113 | return false; 114 | } 115 | 116 | return true; 117 | } 118 | 119 | private string GET_FUNC_POINTER(string GuidValue, string pft, string socket = "socket") { 120 | string str = " guid = " ~ GuidValue ~ ";"; 121 | str ~= "if( !GetFunctionPointer( " ~ socket ~ ", " ~ pft 122 | ~ ", guid ) ) { errnoEnforce(false,\"get function error!\"); } "; 123 | return str; 124 | } 125 | 126 | enum : DWORD { 127 | IOCPARAM_MASK = 0x7f, 128 | IOC_VOID = 0x20000000, 129 | IOC_OUT = 0x40000000, 130 | IOC_IN = 0x80000000, 131 | IOC_INOUT = IOC_IN | IOC_OUT 132 | } 133 | 134 | enum IOC_UNIX = 0x00000000; 135 | enum IOC_WS2 = 0x08000000; 136 | enum IOC_PROTOCOL = 0x10000000; 137 | enum IOC_VENDOR = 0x18000000; 138 | 139 | template _WSAIO(int x, int y) { 140 | enum _WSAIO = IOC_VOID | x | y; 141 | } 142 | 143 | template _WSAIOR(int x, int y) { 144 | enum _WSAIOR = IOC_OUT | x | y; 145 | } 146 | 147 | template _WSAIOW(int x, int y) { 148 | enum _WSAIOW = IOC_IN | x | y; 149 | } 150 | 151 | template _WSAIORW(int x, int y) { 152 | enum _WSAIORW = IOC_INOUT | x | y; 153 | } 154 | 155 | enum SIO_ASSOCIATE_HANDLE = _WSAIOW!(IOC_WS2, 1); 156 | enum SIO_ENABLE_CIRCULAR_QUEUEING = _WSAIO!(IOC_WS2, 2); 157 | enum SIO_FIND_ROUTE = _WSAIOR!(IOC_WS2, 3); 158 | enum SIO_FLUSH = _WSAIO!(IOC_WS2, 4); 159 | enum SIO_GET_BROADCAST_ADDRESS = _WSAIOR!(IOC_WS2, 5); 160 | enum SIO_GET_EXTENSION_FUNCTION_POINTER = _WSAIORW!(IOC_WS2, 6); 161 | enum SIO_GET_QOS = _WSAIORW!(IOC_WS2, 7); 162 | enum SIO_GET_GROUP_QOS = _WSAIORW!(IOC_WS2, 8); 163 | enum SIO_MULTIPOINT_LOOPBACK = _WSAIOW!(IOC_WS2, 9); 164 | enum SIO_MULTICAST_SCOPE = _WSAIOW!(IOC_WS2, 10); 165 | enum SIO_SET_QOS = _WSAIOW!(IOC_WS2, 11); 166 | enum SIO_SET_GROUP_QOS = _WSAIOW!(IOC_WS2, 12); 167 | enum SIO_TRANSLATE_HANDLE = _WSAIORW!(IOC_WS2, 13); 168 | enum SIO_ROUTING_INTERFACE_QUERY = _WSAIORW!(IOC_WS2, 20); 169 | enum SIO_ROUTING_INTERFACE_CHANGE = _WSAIOW!(IOC_WS2, 21); 170 | enum SIO_ADDRESS_LIST_QUERY = _WSAIOR!(IOC_WS2, 22); 171 | enum SIO_ADDRESS_LIST_CHANGE = _WSAIO!(IOC_WS2, 23); 172 | enum SIO_QUERY_TARGET_PNP_HANDLE = _WSAIOR!(IOC_WS2, 24); 173 | enum SIO_NSP_NOTIFY_CHANGE = _WSAIOW!(IOC_WS2, 25); 174 | 175 | extern (Windows): 176 | int WSARecv(SOCKET, LPWSABUF, DWORD, LPDWORD, LPDWORD, LPWSAOVERLAPPED, 177 | LPWSAOVERLAPPED_COMPLETION_ROUTINE); 178 | int WSARecvDisconnect(SOCKET, LPWSABUF); 179 | int WSARecvFrom(SOCKET, LPWSABUF, DWORD, LPDWORD, LPDWORD, SOCKADDR*, LPINT, 180 | LPWSAOVERLAPPED, LPWSAOVERLAPPED_COMPLETION_ROUTINE); 181 | 182 | int WSASend(SOCKET, LPWSABUF, DWORD, LPDWORD, DWORD, LPWSAOVERLAPPED, 183 | LPWSAOVERLAPPED_COMPLETION_ROUTINE); 184 | int WSASendDisconnect(SOCKET, LPWSABUF); 185 | int WSASendTo(SOCKET, LPWSABUF, DWORD, LPDWORD, DWORD, const(SOCKADDR)*, int, 186 | LPWSAOVERLAPPED, LPWSAOVERLAPPED_COMPLETION_ROUTINE); 187 | -------------------------------------------------------------------------------- /source/hunt/io/channel/iocp/package.d: -------------------------------------------------------------------------------- 1 | module hunt.io.channel.iocp; 2 | 3 | // dfmt off 4 | version (HAVE_IOCP) : 5 | // dfmt on 6 | 7 | public import hunt.io.channel.iocp.AbstractDatagramSocket; 8 | public import hunt.io.channel.iocp.AbstractListener; 9 | public import hunt.io.channel.iocp.AbstractStream; 10 | public import hunt.io.channel.iocp.Common; -------------------------------------------------------------------------------- /source/hunt/io/channel/package.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A refined core library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.io.channel; 13 | 14 | public import hunt.io.channel.AbstractChannel; 15 | public import hunt.io.channel.AbstractSocketChannel; 16 | public import hunt.io.channel.Common; 17 | 18 | version (Posix) { 19 | public import hunt.io.channel.posix; 20 | } else version (Windows) { 21 | public import hunt.io.channel.iocp; 22 | } 23 | -------------------------------------------------------------------------------- /source/hunt/io/channel/posix/AbstractDatagramSocket.d: -------------------------------------------------------------------------------- 1 | module hunt.io.channel.posix.AbstractDatagramSocket; 2 | 3 | // dfmt off 4 | version(Posix): 5 | // dfmt on 6 | 7 | import hunt.event.selector.Selector; 8 | import hunt.Functions; 9 | import hunt.io.channel.AbstractSocketChannel; 10 | import hunt.io.channel.Common; 11 | import hunt.logging; 12 | 13 | import std.socket; 14 | 15 | /** 16 | UDP Socket 17 | */ 18 | abstract class AbstractDatagramSocket : AbstractSocketChannel { 19 | this(Selector loop, AddressFamily family = AddressFamily.INET, int bufferSize = 4096 * 2) { 20 | super(loop, ChannelType.UDP); 21 | setFlag(ChannelFlag.Read, true); 22 | setFlag(ChannelFlag.ETMode, false); 23 | 24 | this.socket = new UdpSocket(family); 25 | // _socket.blocking = false; 26 | _readBuffer = new UdpDataObject(); 27 | _readBuffer.data = new ubyte[bufferSize]; 28 | 29 | if (family == AddressFamily.INET) 30 | _bindAddress = new InternetAddress(InternetAddress.PORT_ANY); 31 | else if (family == AddressFamily.INET6) 32 | _bindAddress = new Internet6Address(Internet6Address.PORT_ANY); 33 | else 34 | _bindAddress = new UnknownAddress(); 35 | } 36 | 37 | final void bind(Address addr) { 38 | if (_binded) 39 | return; 40 | _bindAddress = addr; 41 | socket.bind(_bindAddress); 42 | _binded = true; 43 | } 44 | 45 | final bool isBind() { 46 | return _binded; 47 | } 48 | 49 | Address bindAddr() { 50 | return _bindAddress; 51 | } 52 | 53 | protected UdpDataObject _readBuffer; 54 | protected bool _binded = false; 55 | protected Address _bindAddress; 56 | 57 | protected bool tryRead(scope SimpleActionHandler read) { 58 | this._readBuffer.addr = createAddress(this.socket.addressFamily, 0); 59 | auto data = this._readBuffer.data; 60 | scope (exit) 61 | this._readBuffer.data = data; 62 | // auto len = this.socket.receiveFrom(this._readBuffer.data, this._readBuffer.addr); 63 | 64 | auto len = this.socket.receiveFrom(this._readBuffer.data, this._readBuffer.addr); 65 | if (len > 0) { 66 | this._readBuffer.data = this._readBuffer.data[0 .. len]; 67 | read(this._readBuffer); 68 | } 69 | return false; 70 | } 71 | 72 | override void onWrite() { 73 | version (HUNT_DEBUG) 74 | tracef("try to write [fd=%d]", this.handle); 75 | } 76 | } -------------------------------------------------------------------------------- /source/hunt/io/channel/posix/AbstractListener.d: -------------------------------------------------------------------------------- 1 | module hunt.io.channel.posix.AbstractListener; 2 | 3 | // dfmt off 4 | version(Posix): 5 | // dfmt on 6 | 7 | import hunt.event.selector.Selector; 8 | import hunt.io.channel.AbstractSocketChannel; 9 | import hunt.io.channel.Common; 10 | import hunt.logging; 11 | 12 | import std.conv; 13 | import std.socket; 14 | 15 | import core.sys.posix.sys.socket; 16 | 17 | 18 | /** 19 | * TCP Server 20 | */ 21 | abstract class AbstractListener : AbstractSocketChannel { 22 | this(Selector loop, AddressFamily family = AddressFamily.INET) { 23 | super(loop, ChannelType.Accept); 24 | setFlag(ChannelFlag.Read, true); 25 | this.socket = new TcpSocket(family); 26 | } 27 | 28 | protected bool onAccept(scope AcceptHandler handler) { 29 | version (HUNT_DEBUG) 30 | trace("new connection coming..."); 31 | this.clearError(); 32 | // http://man7.org/linux/man-pages/man2/accept.2.html 33 | version(HAVE_EPOLL) { 34 | socket_t clientFd = cast(socket_t)(accept4(this.handle, null, null, SOCK_NONBLOCK | SOCK_CLOEXEC)); 35 | // socket_t clientFd = cast(socket_t)(accept(this.handle, null, null)); 36 | } else { 37 | socket_t clientFd = cast(socket_t)(accept(this.handle, null, null)); 38 | } 39 | if (clientFd == socket_t.init) 40 | return false; 41 | 42 | version (HUNT_DEBUG) 43 | tracef("Listener fd=%d, client fd=%d", this.handle, clientFd); 44 | 45 | if (handler !is null) 46 | handler(new Socket(clientFd, this.localAddress.addressFamily)); 47 | return true; 48 | } 49 | 50 | override void onWriteDone() { 51 | version (HUNT_DEBUG) 52 | tracef("a new connection created"); 53 | } 54 | } 55 | 56 | 57 | extern (C) nothrow @nogc { 58 | int accept4(int, sockaddr*, socklen_t*, int); 59 | } 60 | 61 | enum int SOCK_CLOEXEC = std.conv.octal!(2000000); /* Atomically set close-on-exec flag for the 62 | new descriptor(s). */ 63 | enum int SOCK_NONBLOCK = std.conv.octal!4000; /* Atomically mark descriptor(s) as 64 | non-blocking. */ 65 | -------------------------------------------------------------------------------- /source/hunt/io/channel/posix/EpollEventChannel.d: -------------------------------------------------------------------------------- 1 | module hunt.io.channel.posix.EpollEventChannel; 2 | 3 | // dfmt off 4 | version (HAVE_EPOLL) : 5 | // dfmt on 6 | 7 | import hunt.event.selector.Selector; 8 | import hunt.io.channel.Common; 9 | import hunt.io.channel.AbstractChannel; 10 | import hunt.logging; 11 | 12 | // import std.conv; 13 | import std.socket; 14 | import core.sys.posix.unistd; 15 | import core.sys.linux.sys.eventfd; 16 | 17 | /** 18 | https://stackoverflow.com/questions/5355791/linux-cant-get-eventfd-to-work-with-epoll-together 19 | */ 20 | class EpollEventChannel : EventChannel { 21 | this(Selector loop) { 22 | super(loop); 23 | setFlag(ChannelFlag.Read, true); 24 | this.handle = cast(socket_t)eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); 25 | _isRegistered = true; 26 | } 27 | 28 | ~this() { 29 | // close(); 30 | } 31 | 32 | override void trigger() { 33 | version (HUNT_IO_DEBUG) tracef("trigger the epoll selector."); 34 | int r = eventfd_write(this.handle, 1); 35 | if(r != 0) { 36 | warningf("error: %d", r); 37 | } 38 | } 39 | 40 | override void onWrite() { 41 | version (HUNT_IO_DEBUG) tracef("eventLoop running: %s, [fd=%d]", eventLoop.isRuning, this.handle); 42 | version (HUNT_IO_DEBUG) warning("do nothing"); 43 | } 44 | 45 | override void onRead() { 46 | this.clearError(); 47 | uint64_t value; 48 | int r = eventfd_read(this.handle, &value); 49 | version (HUNT_IO_DEBUG) { 50 | tracef("result=%d, value=%d, fd=%d", r, value, this.handle); 51 | if(r != 0) { 52 | warningf("error: %d", r); 53 | } 54 | } 55 | } 56 | 57 | override void onClose() { 58 | version (HUNT_IO_DEBUG) tracef("onClose, [fd=%d]...", this.handle); 59 | super.onClose(); 60 | core.sys.posix.unistd.close(this.handle); 61 | version (HUNT_IO_DEBUG) tracef("onClose done, [fd=%d]", this.handle); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /source/hunt/io/channel/posix/KqueueEventChannel.d: -------------------------------------------------------------------------------- 1 | module hunt.io.channel.posix.KqueueEventChannel; 2 | 3 | // dfmt off 4 | version(HAVE_KQUEUE): 5 | // dfmt on 6 | 7 | import hunt.event.selector.Selector; 8 | import hunt.io.channel.AbstractChannel; 9 | import hunt.io.channel.Common; 10 | 11 | import std.socket; 12 | 13 | /** 14 | */ 15 | class KqueueEventChannel : EventChannel { 16 | this(Selector loop) { 17 | super(loop); 18 | setFlag(ChannelFlag.Read, true); 19 | _pair = socketPair(); 20 | _pair[0].blocking = false; 21 | _pair[1].blocking = false; 22 | this.handle = _pair[1].handle; 23 | } 24 | 25 | ~this() @nogc { 26 | // close(); 27 | } 28 | 29 | override void trigger() { 30 | _pair[0].send("call"); 31 | } 32 | 33 | override void onRead() { 34 | ubyte[128] data; 35 | while (true) { 36 | if (_pair[1].receive(data) <= 0) 37 | break; 38 | } 39 | } 40 | 41 | Socket[2] _pair; 42 | } -------------------------------------------------------------------------------- /source/hunt/io/channel/posix/package.d: -------------------------------------------------------------------------------- 1 | module hunt.io.channel.posix; 2 | 3 | // dfmt off 4 | version(Posix): 5 | // dfmt on 6 | 7 | public import hunt.io.channel.posix.AbstractDatagramSocket; 8 | public import hunt.io.channel.posix.AbstractListener; 9 | public import hunt.io.channel.posix.AbstractStream; 10 | 11 | version (HAVE_EPOLL) { 12 | public import hunt.io.channel.posix.EpollEventChannel; 13 | } 14 | 15 | version(HAVE_KQUEUE) { 16 | public import hunt.io.channel.posix.KqueueEventChannel; 17 | } -------------------------------------------------------------------------------- /source/hunt/io/package.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A refined core library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.io; 13 | 14 | public import hunt.io.IoError; 15 | public import hunt.io.TcpListener; 16 | public import hunt.io.TcpStream; 17 | public import hunt.io.TcpStreamOptions; 18 | -------------------------------------------------------------------------------- /source/hunt/logging/ConsoleLogger.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A refined core library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | deprecated("Using hunt.logging.Logger instead.") 12 | module hunt.logging.ConsoleLogger; 13 | 14 | public import hunt.logging.Logger; -------------------------------------------------------------------------------- /source/hunt/logging/Helper.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A refined core library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.logging.Helper; 13 | import hunt.logging.Logger; 14 | 15 | import core.runtime; 16 | import core.stdc.stdlib; 17 | import std.exception; 18 | 19 | void catchAndLogException(E)(lazy E runer) @trusted nothrow 20 | { 21 | try 22 | { 23 | runer(); 24 | } 25 | catch (Exception e) 26 | { 27 | collectException(warning(e.toString)); 28 | } 29 | catch (Error e) 30 | { 31 | collectException(() { error(e.toString); rt_term(); }()); 32 | exit(-1); 33 | } 34 | } -------------------------------------------------------------------------------- /source/hunt/logging/package.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A refined core library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.logging; 13 | 14 | public import hunt.logging.Logger; 15 | public import hunt.logging.Helper; 16 | -------------------------------------------------------------------------------- /source/hunt/serialization/BinaryDeserializer.d: -------------------------------------------------------------------------------- 1 | module hunt.serialization.BinaryDeserializer; 2 | 3 | import hunt.serialization.Common; 4 | import hunt.serialization.Specify; 5 | import std.traits; 6 | 7 | import hunt.logging; 8 | 9 | /** 10 | * 11 | */ 12 | struct BinaryDeserializer { 13 | 14 | private { 15 | const(ubyte)[] _buffer; 16 | } 17 | 18 | this(ubyte[] buffer) { 19 | this._buffer = buffer; 20 | } 21 | 22 | const(ubyte[]) bytes() const nothrow { 23 | return _buffer; 24 | } 25 | 26 | ulong bytesLeft() const { 27 | return _buffer.length; 28 | } 29 | 30 | T iArchive(SerializationOptions options, T)() 31 | if (!isDynamicArray!T && !isAssociativeArray!T && !is(T == class) && __traits(compiles, T())) { 32 | T obj; 33 | specify!(options)(this, obj); 34 | return obj; 35 | } 36 | 37 | T iArchive(SerializationOptions options, T)() 38 | if (!isDynamicArray!T && !isAssociativeArray!T && !is(T == class) 39 | && !__traits(compiles, T())) { 40 | T obj = void; 41 | specify!(options)(this, obj); 42 | return obj; 43 | } 44 | 45 | T iArchive(SerializationOptions options, T, A...)(A args) if (is(T == class)) { 46 | T obj = new T(args); 47 | specify!(options)(this, obj); 48 | return obj; 49 | } 50 | 51 | T iArchive(SerializationOptions options, T)() if (isDynamicArray!T || isAssociativeArray!T) { 52 | return iArchive!(options, T, ushort)(); 53 | } 54 | 55 | T iArchive(SerializationOptions options, T, U)() if (isDynamicArray!T || isAssociativeArray!T) { 56 | T obj; 57 | specify!(options)(this, obj); 58 | return obj; 59 | } 60 | 61 | void putUbyte(ref ubyte val) { 62 | val = _buffer[0]; 63 | _buffer = _buffer[1 .. $]; 64 | } 65 | 66 | void putClass(SerializationOptions options, T)(T val) if (is(T == class)) { 67 | specifyClass!(options)(this, val); 68 | } 69 | 70 | deprecated("Using take instead.") 71 | alias putRaw = take; 72 | 73 | const(ubyte)[] take(size_t length) { 74 | const(ubyte)[] res = _buffer[0 .. length]; 75 | _buffer = _buffer[length .. $]; 76 | return res; 77 | } 78 | 79 | // T takeAs(T, SerializationOptions options)() 80 | // if (!is(T == enum) && (isSigned!T || isBoolean!T || is(T == char) || isFloatingPoint!T)) { 81 | // T r; 82 | // // T* val = &r; 83 | 84 | // // ubyte val0 = (val >> 24); 85 | // // ubyte val1 = cast(ubyte)(val >> 16); 86 | // // ubyte val2 = cast(ubyte)(val >> 8); 87 | // // ubyte val3 = val & 0xff; 88 | // // putUbyte(val0); 89 | // // putUbyte(val1); 90 | // // putUbyte(val2); 91 | // // putUbyte(val3); 92 | // // val = (val0 << 24) + (val1 << 16) + (val2 << 8) + val3; 93 | // return r; 94 | // } 95 | 96 | // T takeAs(T, SerializationOptions options)() { 97 | // return T.init; 98 | // } 99 | 100 | bool isNullObj() { 101 | return _buffer[0 .. 4] == NULL ? true : false; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /source/hunt/serialization/BinarySerialization.d: -------------------------------------------------------------------------------- 1 | module hunt.serialization.BinarySerialization; 2 | 3 | import hunt.serialization.BinarySerializer; 4 | import hunt.serialization.BinaryDeserializer; 5 | import hunt.serialization.Common; 6 | 7 | import std.traits; 8 | 9 | ubyte[] serialize(SerializationOptions options = SerializationOptions.Full, T)(T obj) { 10 | auto serializer = BinarySerializer(); 11 | return serializer.oArchive!(options)(obj); 12 | } 13 | 14 | T unserialize(T, SerializationOptions options = SerializationOptions.Full)(ubyte[] buffer) { 15 | auto deserializer = BinaryDeserializer(buffer); 16 | return deserializer.iArchive!(options, T); 17 | } 18 | 19 | 20 | alias toObject = unserialize; 21 | alias toBinary = serialize; -------------------------------------------------------------------------------- /source/hunt/serialization/BinarySerializer.d: -------------------------------------------------------------------------------- 1 | module hunt.serialization.BinarySerializer; 2 | 3 | import std.array : Appender; 4 | import std.traits; 5 | 6 | import hunt.serialization.Common; 7 | import hunt.serialization.Specify; 8 | 9 | 10 | /** 11 | * 12 | */ 13 | struct BinarySerializer { 14 | private { 15 | Appender!(ubyte[]) _buffer; 16 | } 17 | 18 | ubyte[] oArchive(SerializationOptions options, T)(T val) if (!isArray!T && !isAssociativeArray!T) { 19 | Unqual!T copy = val; 20 | specify!(options)(this, copy); 21 | return _buffer.data(); 22 | } 23 | 24 | ubyte[] oArchive(SerializationOptions options, T)(const ref T val) 25 | if (!isDynamicArray!T && !isAssociativeArray!T && !isAggregateType!T) { 26 | T copy = val; 27 | specify!(options)(this, copy); 28 | return _buffer.data(); 29 | } 30 | 31 | ubyte[] oArchive(SerializationOptions options, T)(const(T)[] val) { 32 | auto copy = (cast(T[]) val).dup; 33 | specify!(options)(this, copy); 34 | return _buffer.data(); 35 | } 36 | 37 | ubyte[] oArchive(SerializationOptions options, K, V)(const(V[K]) val) { 38 | auto copy = cast(V[K]) val.dup; 39 | specify!(options)(this, copy); 40 | return _buffer.data(); 41 | } 42 | 43 | void putUbyte(ref ubyte val) { 44 | _buffer.put(val); 45 | } 46 | 47 | void putClass(SerializationOptions options, T)(T val) if (is(T == class)) { 48 | specifyClass!(options)(this, val); 49 | } 50 | 51 | void putRaw(ubyte[] val) { 52 | _buffer.put(val); 53 | } 54 | 55 | bool isNullObj() { 56 | return false; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /source/hunt/serialization/Common.d: -------------------------------------------------------------------------------- 1 | module hunt.serialization.Common; 2 | 3 | import std.typecons : Flag; 4 | 5 | 6 | /** 7 | * 8 | */ 9 | struct SerializationOptions { 10 | enum Default = SerializationOptions(); 11 | enum Full = SerializationOptions().includeMeta(true); 12 | enum Lite = SerializationOptions().traverseBase(false).ignoreNull(true).depth(0); 13 | enum Normal = SerializationOptions().ignoreNull(true); 14 | 15 | enum OnlyPublicWithNull = SerializationOptions().onlyPublic(true).traverseBase(false).depth(0); 16 | enum OnlyPublicLite = OnlyPublicWithNull.ignoreNull(true); 17 | 18 | private bool _onlyPublic = false; 19 | 20 | private bool _traverseBase = true; 21 | 22 | private bool _includeMeta = false; 23 | 24 | private bool _ignoreNull = false; 25 | 26 | private bool _canThrow = true; 27 | 28 | private bool _canCircularDetect = true; // Circular Reference Detect 29 | 30 | private int _depth = -1; 31 | 32 | /* --------------------------------------------------- properties --------------------------------------------------- */ 33 | 34 | bool onlyPublic() { return _onlyPublic; } 35 | 36 | SerializationOptions onlyPublic(bool flag) { 37 | SerializationOptions r = this; 38 | r._onlyPublic = flag; 39 | return r; 40 | } 41 | 42 | bool traverseBase() { return _traverseBase; } 43 | 44 | SerializationOptions traverseBase(bool flag) { 45 | SerializationOptions r = this; 46 | r._traverseBase = flag; 47 | return r; 48 | } 49 | 50 | bool includeMeta() { return _includeMeta; } 51 | 52 | SerializationOptions includeMeta(bool flag) { 53 | SerializationOptions r = this; 54 | r._includeMeta = flag; 55 | return r; 56 | } 57 | 58 | bool ignoreNull() { return _ignoreNull; } 59 | 60 | SerializationOptions ignoreNull(bool flag) { 61 | SerializationOptions r = this; 62 | r._ignoreNull = flag; 63 | return r; 64 | } 65 | 66 | bool canThrow() { return _canThrow; } 67 | 68 | SerializationOptions canThrow(bool flag) { 69 | SerializationOptions r = this; 70 | r._canThrow = flag; 71 | return r; 72 | } 73 | 74 | bool canCircularDetect() { return _canCircularDetect; } 75 | 76 | SerializationOptions canCircularDetect(bool flag) { 77 | SerializationOptions r = this; 78 | r._canCircularDetect = flag; 79 | return r; 80 | } 81 | 82 | int depth() { return _depth; } 83 | 84 | SerializationOptions depth(int depth) { 85 | SerializationOptions r = this; 86 | r._depth = depth; 87 | return r; 88 | } 89 | } 90 | 91 | /** 92 | Flag indicating whether to traverse the base class. 93 | */ 94 | alias TraverseBase = Flag!"traverseBase"; 95 | 96 | /** 97 | Flag indicating whether to allow the public member only. 98 | */ 99 | alias OnlyPublic = Flag!"onlyPublic"; 100 | 101 | /** 102 | Flag indicating whether to include the meta data (especially for a class or an interface). 103 | */ 104 | alias IncludeMeta = Flag!"includeMeta"; 105 | 106 | /** 107 | Flag indicating whether to ignore the null member. 108 | */ 109 | alias IgnoreNull = Flag!"ignoreNull"; 110 | 111 | 112 | /// attributes for json 113 | /// https://dzone.com/articles/jackson-annotations-for-json-part-2-serialization 114 | 115 | /** 116 | * Excludes the field from both encoding and decoding. 117 | */ 118 | enum Ignore; 119 | 120 | deprecated("Using Ignore instead.") 121 | alias Exclude = Ignore; 122 | 123 | /** 124 | * Includes this even if it would otherwise be excluded. 125 | * If Exclude (or other UDA(@)) and Include are present value will be included. 126 | * Can also be used on @property methods to include them. (Be sure both the setter and getter exist!) 127 | * If used on a value of a base class value will be included. 128 | */ 129 | enum Include; 130 | 131 | /** 132 | * Excludes the field from decoding, encode only. 133 | */ 134 | enum EncodeOnly; 135 | 136 | /** 137 | * Excludes the field from encoding, decode only. 138 | */ 139 | enum DecodeOnly; -------------------------------------------------------------------------------- /source/hunt/serialization/package.d: -------------------------------------------------------------------------------- 1 | module hunt.serialization; 2 | 3 | public import hunt.serialization.Common; 4 | public import hunt.serialization.BinarySerialization; 5 | public import hunt.serialization.JsonSerializer; 6 | -------------------------------------------------------------------------------- /source/hunt/system/Error.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A refined core library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.system.Error; 13 | 14 | import core.stdc.string; 15 | 16 | version (Windows) { 17 | import core.sys.windows.winnt; 18 | /// https://docs.microsoft.com/zh-cn/windows/desktop/Intl/language-identifier-constants-and-strings 19 | string getErrorMessage(bool useStdC = false)(int errno, int langId = LANG_ENGLISH) @trusted { 20 | 21 | static if (useStdC) { 22 | auto s = strerror(errno); 23 | return s[0 .. s.strlen].idup; 24 | } else { 25 | import std.windows.syserror; 26 | 27 | return sysErrorString(errno, langId); 28 | } 29 | } 30 | } else { 31 | string getErrorMessage(int errno) @trusted { 32 | version (Posix) { 33 | import core.stdc.errno; 34 | import std.conv: to; 35 | 36 | char[80] buf; 37 | const(char)* cs; 38 | version (CRuntime_Glibc) { 39 | cs = strerror_r(errno, buf.ptr, buf.length); 40 | } else { 41 | auto errs = strerror_r(errno, buf.ptr, buf.length); 42 | if (errs == 0) 43 | cs = buf.ptr; 44 | else 45 | return to!string(errno); 46 | } 47 | 48 | auto len = strlen(cs); 49 | 50 | if (cs[len - 1] == '\n') 51 | len--; 52 | if (cs[len - 1] == '\r') 53 | len--; 54 | return cs[0 .. len].idup; 55 | } else { 56 | auto s = strerror(errno); 57 | return s[0 .. s.strlen].idup; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /source/hunt/system/Memory.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A refined core library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.system.Memory; 13 | 14 | import core.atomic; 15 | import core.memory; 16 | 17 | import std.traits; 18 | 19 | version (OSX) { 20 | version = useSysctlbyname; 21 | } 22 | else version (FreeBSD) { 23 | version = useSysctlbyname; 24 | } 25 | else version (DragonFlyBSD) { 26 | version = useSysctlbyname; 27 | } 28 | else version (NetBSD) { 29 | version = useSysctlbyname; 30 | } 31 | 32 | version(useSysctlbyname) 33 | private extern(C) int sysctlbyname( 34 | const char *, void *, size_t *, void *, size_t 35 | ) @nogc nothrow; 36 | 37 | /* 38 | (For now public undocumented with reserved name.) 39 | 40 | A lazily initialized global constant. The underlying value is a shared global 41 | statically initialized to `outOfBandValue` which must not be a legit value of 42 | the constant. Upon the first call the situation is detected and the global is 43 | initialized by calling `initializer`. The initializer is assumed to be pure 44 | (even if not marked as such), i.e. return the same value upon repeated calls. 45 | For that reason, no special precautions are taken so `initializer` may be called 46 | more than one time leading to benign races on the cached value. 47 | 48 | In the quiescent state the cost of the function is an atomic load from a global. 49 | 50 | Params: 51 | T = The type of the pseudo-constant (may be qualified) 52 | outOfBandValue = A value that cannot be valid, it is used for initialization 53 | initializer = The function performing initialization; must be `nothrow` 54 | 55 | Returns: 56 | The lazily initialized value 57 | */ 58 | @property pure T __lazilyInitializedConstant(T, alias outOfBandValue, alias initializer)() 59 | if (is(Unqual!T : T) && is(typeof(initializer()) : T) && is(typeof(outOfBandValue) : T)) { 60 | static T impl() nothrow { 61 | // Thread-local cache 62 | static Unqual!T tls = outOfBandValue; 63 | auto local = tls; 64 | // Shortest path, no atomic operations 65 | if (local != outOfBandValue) 66 | return local; 67 | // Process-level cache 68 | static shared Unqual!T result = outOfBandValue; 69 | // Initialize both process-level cache and tls 70 | local = atomicLoad(result); 71 | if (local == outOfBandValue) { 72 | local = initializer(); 73 | atomicStore(result, local); 74 | } 75 | tls = local; 76 | return local; 77 | } 78 | 79 | // import std.traits : SetFunctionAttributes; 80 | alias Fun = SetFunctionAttributes!(typeof(&impl), "D", 81 | functionAttributes!(typeof(&impl)) | FunctionAttribute.pure_); 82 | auto purified = (() @trusted => cast(Fun)&impl)(); 83 | return purified(); 84 | } 85 | 86 | /** 87 | The total number of CPU cores available on the current machine, as reported by 88 | the operating system. 89 | */ 90 | alias totalCPUs = __lazilyInitializedConstant!(immutable(uint), uint.max, totalCPUsImpl); 91 | 92 | uint totalCPUsImpl() @nogc nothrow @trusted { 93 | version (Windows) { 94 | // BUGS: Only works on Windows 2000 and above. 95 | import core.sys.windows.windows : SYSTEM_INFO, GetSystemInfo; 96 | import std.algorithm.comparison : max; 97 | 98 | SYSTEM_INFO si; 99 | GetSystemInfo(&si); 100 | return max(1, cast(uint) si.dwNumberOfProcessors); 101 | } 102 | else version (linux) { 103 | import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf; 104 | 105 | return cast(uint) sysconf(_SC_NPROCESSORS_ONLN); 106 | } 107 | else version (Solaris) { 108 | import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf; 109 | 110 | return cast(uint) sysconf(_SC_NPROCESSORS_ONLN); 111 | } 112 | else version (useSysctlbyname) { 113 | version (OSX) { 114 | auto nameStr = "machdep.cpu.core_count\0".ptr; 115 | } 116 | else version (FreeBSD) { 117 | auto nameStr = "hw.ncpu\0".ptr; 118 | } 119 | else version (DragonFlyBSD) { 120 | auto nameStr = "hw.ncpu\0".ptr; 121 | } 122 | else version (NetBSD) { 123 | auto nameStr = "hw.ncpu\0".ptr; 124 | } 125 | 126 | uint result; 127 | size_t len = result.sizeof; 128 | sysctlbyname(nameStr, &result, &len, null, 0); 129 | return result; 130 | } 131 | else { 132 | static assert(0, "Don't know how to get N CPUs on this OS."); 133 | } 134 | } 135 | 136 | /** 137 | */ 138 | size_t pageSize() @safe pure nothrow @nogc { 139 | return _pageSize; 140 | } 141 | 142 | static immutable size_t _pageSize; 143 | 144 | shared static this() { 145 | version (Windows) { 146 | import core.sys.windows.winbase; 147 | 148 | SYSTEM_INFO info; 149 | GetSystemInfo(&info); 150 | 151 | _pageSize = info.dwPageSize; 152 | assert(_pageSize < int.max); 153 | } 154 | else version (Posix) { 155 | import core.sys.posix.unistd; 156 | 157 | _pageSize = cast(size_t) sysconf(_SC_PAGESIZE); 158 | } 159 | else { 160 | static assert(0, "unimplemented"); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /source/hunt/system/WindowsHelper.d: -------------------------------------------------------------------------------- 1 | module hunt.system.WindowsHelper; 2 | 3 | // dfmt off 4 | version (Windows): 5 | // dfmt on 6 | 7 | import std.exception; 8 | import std.stdio; 9 | import std.windows.charset; 10 | 11 | import core.sys.windows.wincon; 12 | import core.sys.windows.winbase; 13 | import core.sys.windows.windef; 14 | import core.stdc.stdio; 15 | 16 | struct ConsoleHelper { 17 | private __gshared HANDLE g_hout; 18 | enum defaultColor = FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE; 19 | 20 | shared static this() { 21 | g_hout = GetStdHandle(STD_OUTPUT_HANDLE); 22 | resetColor(); 23 | } 24 | 25 | static HANDLE getHandle() nothrow { 26 | return g_hout; 27 | } 28 | 29 | static void resetColor() nothrow { 30 | SetConsoleTextAttribute(g_hout, defaultColor); 31 | } 32 | 33 | static void setTextAttribute(ushort attr) nothrow { 34 | SetConsoleTextAttribute(g_hout, attr); 35 | } 36 | 37 | static void write(lazy string msg) nothrow { 38 | try { 39 | printf("%s\n", toMBSz(msg)); 40 | } catch(Exception ex) { 41 | collectException( { 42 | setTextAttribute(FOREGROUND_RED); 43 | writeln(ex); 44 | setTextAttribute(defaultColor); 45 | }()); 46 | } 47 | } 48 | 49 | static void writeWithAttribute(lazy string msg, ushort attr = defaultColor) nothrow { 50 | setTextAttribute(attr); 51 | try { 52 | printf("%s\n", toMBSz(msg)); 53 | if ((attr & defaultColor) != defaultColor) 54 | resetColor(); 55 | } catch(Exception ex) { 56 | collectException( { 57 | setTextAttribute(FOREGROUND_RED); 58 | writeln(ex); 59 | setTextAttribute(defaultColor); 60 | }()); 61 | } 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /source/hunt/system/package.d: -------------------------------------------------------------------------------- 1 | module hunt.system; 2 | 3 | public import hunt.system.Error; 4 | public import hunt.system.Memory; -------------------------------------------------------------------------------- /source/hunt/system/syscall/package.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A refined core library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.system.syscall; 13 | 14 | @system: 15 | version(Posix): 16 | 17 | extern (C) nothrow @nogc size_t syscall(size_t ident, ...); 18 | 19 | version(X86_64) 20 | { 21 | version(linux) public import hunt.system.syscall.os.Linux; 22 | else version(OSX) public import hunt.system.syscall.os.OSX; 23 | else version(FreeBSD) public import hunt.system.syscall.os.FreeBSD; 24 | else static assert(false, "Not supoorted OS."); 25 | } 26 | else version(AArch64) 27 | { 28 | version(linux) public import hunt.system.syscall.os.Linux; 29 | else version(OSX) public import hunt.system.syscall.os.OSX; 30 | else version(FreeBSD) public import hunt.system.syscall.os.FreeBSD; 31 | else static assert(false, "Not supoorted OS."); 32 | } 33 | else static assert(false, "The syscall() only supoorted for [x86_64,AArch64]."); 34 | -------------------------------------------------------------------------------- /source/hunt/util/Appendable.d: -------------------------------------------------------------------------------- 1 | module hunt.util.Appendable; 2 | 3 | 4 | /** 5 | * An object to which {@code char} sequences and values can be appended. The 6 | * {@code Appendable} interface must be implemented by any class whose 7 | * instances are intended to receive formatted output from a formatter. 8 | * 9 | *

The characters to be appended should be valid Unicode characters as 10 | * described in Unicode Character 11 | * Representation. Note that supplementary characters may be composed of 12 | * multiple 16-bit {@code char} values. 13 | * 14 | *

Appendables are not necessarily safe for multithreaded access. Thread 15 | * safety is the responsibility of classes that extend and implement this 16 | * interface. 17 | * 18 | *

Since this interface may be implemented by existing classes 19 | * with different styles of error handling there is no guarantee that 20 | * errors will be propagated to the invoker. 21 | * 22 | */ 23 | interface Appendable { 24 | 25 | /** 26 | * Appends the specified character sequence to this {@code Appendable}. 27 | * 28 | *

Depending on which class implements the character sequence 29 | * {@code csq}, the entire sequence may not be appended. For 30 | * instance, if {@code csq} is a {@link java.nio.CharBuffer} then 31 | * the subsequence to append is defined by the buffer's position and limit. 32 | * 33 | * @param csq 34 | * The character sequence to append. If {@code csq} is 35 | * {@code null}, then the four characters {@code "null"} are 36 | * appended to this Appendable. 37 | * 38 | * @return A reference to this {@code Appendable} 39 | * 40 | * @throws IOException 41 | * If an I/O error occurs 42 | */ 43 | Appendable append(const(char)[] csq); 44 | 45 | /** 46 | * Appends a subsequence of the specified character sequence to this 47 | * {@code Appendable}. 48 | * 49 | *

An invocation of this method of the form {@code out.append(csq, start, end)} 50 | * when {@code csq} is not {@code null}, behaves in 51 | * exactly the same way as the invocation 52 | * 53 | *

54 |      *     out.append(csq.subSequence(start, end)) 
55 | * 56 | * @param csq 57 | * The character sequence from which a subsequence will be 58 | * appended. If {@code csq} is {@code null}, then characters 59 | * will be appended as if {@code csq} contained the four 60 | * characters {@code "null"}. 61 | * 62 | * @param start 63 | * The index of the first character in the subsequence 64 | * 65 | * @param end 66 | * The index of the character following the last character in the 67 | * subsequence 68 | * 69 | * @return A reference to this {@code Appendable} 70 | * 71 | * @throws IndexOutOfBoundsException 72 | * If {@code start} or {@code end} are negative, {@code start} 73 | * is greater than {@code end}, or {@code end} is greater than 74 | * {@code csq.length()} 75 | * 76 | * @throws IOException 77 | * If an I/O error occurs 78 | */ 79 | Appendable append(const(char)[], int start, int end); 80 | 81 | /** 82 | * Appends the specified character to this {@code Appendable}. 83 | * 84 | * @param c 85 | * The character to append 86 | * 87 | * @return A reference to this {@code Appendable} 88 | * 89 | * @throws IOException 90 | * If an I/O error occurs 91 | */ 92 | Appendable append(char c); 93 | } 94 | -------------------------------------------------------------------------------- /source/hunt/util/ByteOrder.d: -------------------------------------------------------------------------------- 1 | module hunt.util.ByteOrder; 2 | 3 | 4 | /** 5 | * 6 | */ 7 | enum ByteOrder { 8 | BigEndian, 9 | LittleEndian 10 | } -------------------------------------------------------------------------------- /source/hunt/util/Closeable.d: -------------------------------------------------------------------------------- 1 | module hunt.util.Closeable; 2 | 3 | /** 4 | * An object that may hold resources (such as file or socket handles) 5 | * until it is closed. The {@link #close()} method of an {@code AutoCloseable} 6 | * object is called automatically when exiting a {@code 7 | * try}-with-resources block for which the object has been declared in 8 | * the resource specification header. This construction ensures prompt 9 | * release, avoiding resource exhaustion exceptions and errors that 10 | * may otherwise occur. 11 | */ 12 | interface AutoCloseable { 13 | /** 14 | * Closes this resource, relinquishing any underlying resources. 15 | * This method is invoked automatically on objects managed by the 16 | * {@code try}-with-resources statement. 17 | */ 18 | void close(); 19 | } 20 | 21 | 22 | interface Closeable : AutoCloseable { 23 | 24 | } -------------------------------------------------------------------------------- /source/hunt/util/CompilerHelper.d: -------------------------------------------------------------------------------- 1 | module hunt.util.CompilerHelper; 2 | 3 | 4 | /** 5 | * 6 | */ 7 | class CompilerHelper { 8 | 9 | static bool isGreaterThan(int ver) pure @safe @nogc nothrow { 10 | return __VERSION__ >= ver; 11 | } 12 | 13 | static bool isLessThan(int ver) pure @safe @nogc nothrow { 14 | return __VERSION__ <= ver; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /source/hunt/util/ConverterUtils.d: -------------------------------------------------------------------------------- 1 | module hunt.util.ConverterUtils; 2 | 3 | import hunt.Exceptions; 4 | import hunt.util.Appendable; 5 | import hunt.util.StringBuilder; 6 | import hunt.util.Traits; 7 | 8 | import std.conv; 9 | import std.format; 10 | import std.string; 11 | import std.typecons; 12 | import std.ascii; 13 | 14 | /** 15 | * 16 | */ 17 | struct ConverterUtils { 18 | 19 | /** 20 | * @param c An ASCII encoded character 0-9 a-f A-F 21 | * @return The byte value of the character 0-16. 22 | */ 23 | static byte convertHexDigit(byte c) { 24 | byte b = cast(byte)((c & 0x1f) + ((c >> 6) * 0x19) - 0x10); 25 | if (b < 0 || b > 15) 26 | throw new NumberFormatException("!hex " ~ to!string(c)); 27 | return b; 28 | } 29 | 30 | /* ------------------------------------------------------------ */ 31 | 32 | /** 33 | * @param c An ASCII encoded character 0-9 a-f A-F 34 | * @return The byte value of the character 0-16. 35 | */ 36 | static int convertHexDigit(char c) { 37 | int d = ((c & 0x1f) + ((c >> 6) * 0x19) - 0x10); 38 | if (d < 0 || d > 15) 39 | throw new NumberFormatException("!hex " ~ to!string(c)); 40 | return d; 41 | } 42 | 43 | /* ------------------------------------------------------------ */ 44 | 45 | /** 46 | * @param c An ASCII encoded character 0-9 a-f A-F 47 | * @return The byte value of the character 0-16. 48 | */ 49 | static int convertHexDigit(int c) { 50 | int d = ((c & 0x1f) + ((c >> 6) * 0x19) - 0x10); 51 | if (d < 0 || d > 15) 52 | throw new NumberFormatException("!hex " ~ to!string(c)); 53 | return d; 54 | } 55 | 56 | /* ------------------------------------------------------------ */ 57 | static void toHex(byte b, Appendable buf) { 58 | try { 59 | int d = 0xf & ((0xF0 & b) >> 4); 60 | buf.append(cast(char)((d > 9 ? ('A' - 10) : '0') + d)); 61 | d = 0xf & b; 62 | buf.append(cast(char)((d > 9 ? ('A' - 10) : '0') + d)); 63 | } 64 | catch (IOException e) { 65 | throw new RuntimeException(e); 66 | } 67 | } 68 | 69 | /* ------------------------------------------------------------ */ 70 | static void toHex(int value, Appendable buf) { 71 | int d = 0xf & ((0xF0000000 & value) >> 28); 72 | buf.append(cast(char)((d > 9 ? ('A' - 10) : '0') + d)); 73 | d = 0xf & ((0x0F000000 & value) >> 24); 74 | buf.append(cast(char)((d > 9 ? ('A' - 10) : '0') + d)); 75 | d = 0xf & ((0x00F00000 & value) >> 20); 76 | buf.append(cast(char)((d > 9 ? ('A' - 10) : '0') + d)); 77 | d = 0xf & ((0x000F0000 & value) >> 16); 78 | buf.append(cast(char)((d > 9 ? ('A' - 10) : '0') + d)); 79 | d = 0xf & ((0x0000F000 & value) >> 12); 80 | buf.append(cast(char)((d > 9 ? ('A' - 10) : '0') + d)); 81 | d = 0xf & ((0x00000F00 & value) >> 8); 82 | buf.append(cast(char)((d > 9 ? ('A' - 10) : '0') + d)); 83 | d = 0xf & ((0x000000F0 & value) >> 4); 84 | buf.append(cast(char)((d > 9 ? ('A' - 10) : '0') + d)); 85 | d = 0xf & value; 86 | buf.append(cast(char)((d > 9 ? ('A' - 10) : '0') + d)); 87 | 88 | // Integer.toString(0, 36); 89 | } 90 | 91 | /* ------------------------------------------------------------ */ 92 | static void toHex(long value, Appendable buf) { 93 | toHex(cast(int)(value >> 32), buf); 94 | toHex(cast(int) value, buf); 95 | } 96 | 97 | /* ------------------------------------------------------------ */ 98 | static string toHexString(byte b) { 99 | return toHexString([b], 0, 1); 100 | } 101 | 102 | /* ------------------------------------------------------------ */ 103 | static string toHexString(byte[] b) { 104 | return toHexString(b, 0, cast(int) b.length); 105 | } 106 | 107 | /* ------------------------------------------------------------ */ 108 | static string toHexString(byte[] b, int offset, int length) { 109 | StringBuilder buf = new StringBuilder(); 110 | for (int i = offset; i < offset + length; i++) { 111 | int bi = 0xff & b[i]; 112 | int c = '0' + (bi / 16) % 16; 113 | if (c > '9') 114 | c = 'A' + (c - '0' - 10); 115 | buf.append(cast(char) c); 116 | c = '0' + bi % 16; 117 | if (c > '9') 118 | c = 'a' + (c - '0' - 10); 119 | buf.append(cast(char) c); 120 | } 121 | return buf.toString(); 122 | } 123 | 124 | static string toHexString(LetterCase letterCase = LetterCase.upper)(const(ubyte)[] b, 125 | string separator="", string prefix="") { 126 | static if(letterCase == LetterCase.upper) { 127 | string fmt = "%(" ~ prefix ~ "%02X" ~ separator ~ "%)"; 128 | } else { 129 | string fmt = "%(" ~ prefix ~ "%02x" ~ separator ~ "%)"; 130 | } 131 | 132 | return format(fmt, b); 133 | } 134 | 135 | /* ------------------------------------------------------------ */ 136 | /** 137 | */ 138 | static T[] toBytes(T)(string s) if(isByteType!T) { 139 | if (s.length % 2 != 0) 140 | throw new IllegalArgumentException(s); 141 | T[] r = new T[s.length/2]; 142 | for(size_t i=0; iNOTE: This interface does not imply specific auto-startup semantics. 23 | * Consider implementing {@link SmartLifecycle} for that purpose. 24 | * 25 | *

Can be implemented by both components (typically a Spring bean defined in a 26 | * Spring context) and containers (typically a Spring {@link ApplicationContext} 27 | * itself). Containers will propagate start/stop signals to all components that 28 | * apply within each container, e.g. for a stop/restart scenario at runtime. 29 | * 30 | *

Can be used for direct invocations or for management operations via JMX. 31 | * In the latter case, the {@link org.springframework.jmx.export.MBeanExporter} 32 | * will typically be defined with an 33 | * {@link org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler}, 34 | * restricting the visibility of activity-controlled components to the Lifecycle 35 | * interface. 36 | * 37 | *

Note that the present {@code Lifecycle} interface is only supported on 38 | * top-level singleton beans. On any other component, the {@code Lifecycle} 39 | * interface will remain undetected and hence ignored. Also, note that the extended 40 | * {@link SmartLifecycle} interface provides sophisticated integration with the 41 | * application context's startup and shutdown phases. 42 | * 43 | */ 44 | interface Lifecycle { 45 | /** 46 | * Start this component. 47 | *

Should not throw an exception if the component is already running. 48 | *

In the case of a container, this will propagate the start signal to all 49 | * components that apply. 50 | */ 51 | void start(); 52 | 53 | /** 54 | * Stop this component, typically in a synchronous fashion, such that the component is 55 | * fully stopped upon return of this method. Consider implementing {@link SmartLifecycle} 56 | * and its {@code stop(Runnable)} variant when asynchronous stop behavior is necessary. 57 | *

Note that this stop notification is not guaranteed to come before destruction: 58 | * On regular shutdown, {@code Lifecycle} beans will first receive a stop notification 59 | * before the general destruction callbacks are being propagated; however, on hot 60 | * refresh during a context's lifetime or on aborted refresh attempts, a given bean's 61 | * destroy method will be called without any consideration of stop signals upfront. 62 | *

Should not throw an exception if the component is not running (not started yet). 63 | *

In the case of a container, this will propagate the stop signal to all components 64 | * that apply. 65 | */ 66 | void stop(); 67 | 68 | 69 | /** 70 | * Check whether this component is currently running. 71 | *

In the case of a container, this will return {@code true} only if all 72 | * components that apply are currently running. 73 | * @return whether the component is currently running 74 | */ 75 | bool isRunning(); 76 | } 77 | -------------------------------------------------------------------------------- /source/hunt/util/ResoureManager.d: -------------------------------------------------------------------------------- 1 | module hunt.util.ResoureManager; 2 | 3 | import hunt.logging; 4 | import hunt.util.Closeable; 5 | 6 | import hunt.util.worker.WorkerThread; 7 | 8 | import core.memory; 9 | import core.thread; 10 | 11 | private Closeable[] _closeableObjects; 12 | 13 | void registerResoure(Closeable res) { 14 | assert(res !is null); 15 | foreach (Closeable obj; _closeableObjects) { 16 | if(obj is res) { 17 | version (HUNT_IO_DEBUG) { 18 | tracef("%s@%s has been registered... %d", typeid(cast(Object)res), cast(void*)res); 19 | } 20 | return; 21 | } 22 | } 23 | _closeableObjects ~= res; 24 | } 25 | 26 | void collectResoure() nothrow { 27 | version (HUNT_IO_DEBUG) { 28 | tracef("Collecting (remains: %d)...", _closeableObjects.length); 29 | } 30 | 31 | Closeable[] objects = _closeableObjects; 32 | _closeableObjects = null; 33 | 34 | foreach (obj; objects) { 35 | try { 36 | obj.close(); 37 | } catch (Throwable t) { 38 | warning(t); 39 | } 40 | } 41 | 42 | // GC.collect(); 43 | // GC.minimize(); 44 | } 45 | -------------------------------------------------------------------------------- /source/hunt/util/Runnable.d: -------------------------------------------------------------------------------- 1 | module hunt.util.Runnable; 2 | 3 | 4 | /** 5 | * The Runnable interface should be implemented by any 6 | * class whose instances are intended to be executed by a thread. The 7 | * class must define a method of no arguments called run. 8 | *

9 | * This interface is designed to provide a common protocol for objects that 10 | * wish to execute code while they are active. For example, 11 | * Runnable is implemented by class Thread. 12 | * Being active simply means that a thread has been started and has not 13 | * yet been stopped. 14 | *

15 | * In addition, Runnable provides the means for a class to be 16 | * active while not subclassing Thread. A class that implements 17 | * Runnable can run without subclassing Thread 18 | * by instantiating a Thread instance and passing itself in 19 | * as the target. In most cases, the Runnable interface should 20 | * be used if you are only planning to override the run() 21 | * method and no other Thread methods. 22 | * This is important because classes should not be subclassed 23 | * unless the programmer intends on modifying or enhancing the fundamental 24 | * behavior of the class. 25 | */ 26 | interface Runnable { 27 | /** 28 | * When an object implementing interface Runnable is used 29 | * to create a thread, starting the thread causes the object's 30 | * run method to be called in that separately executing 31 | * thread. 32 | *

33 | * The general contract of the method run is that it may 34 | * take any action whatsoever. 35 | */ 36 | void run(); 37 | } 38 | 39 | -------------------------------------------------------------------------------- /source/hunt/util/StringBuilder.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A refined core library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.util.StringBuilder; 13 | 14 | import hunt.util.Appendable; 15 | 16 | import std.ascii; 17 | import std.algorithm; 18 | import std.array; 19 | import std.exception; 20 | import std.conv; 21 | import std.string; 22 | import std.uni; 23 | 24 | /** 25 | * 26 | */ 27 | class StringBuilder : Appendable { 28 | Appender!(byte[]) _buffer; 29 | 30 | this(size_t capacity = 16) { 31 | _buffer.reserve(capacity); 32 | } 33 | 34 | this(string data, size_t capacity = 16) { 35 | _buffer.reserve(capacity); 36 | this.append(data); 37 | } 38 | 39 | // void append(in char[] s) 40 | // { 41 | // _buffer.put(cast(string) s); 42 | // } 43 | 44 | void reset() { 45 | _buffer.clear(); 46 | } 47 | 48 | StringBuilder setCharAt(int index, char c) { 49 | _buffer.data[index] = c; 50 | return this; 51 | } 52 | 53 | StringBuilder append(char s) { 54 | _buffer.put(s); 55 | return this; 56 | } 57 | 58 | StringBuilder append(bool s) { 59 | append(s.to!string()); 60 | return this; 61 | } 62 | 63 | StringBuilder append(int i) { 64 | _buffer.put(cast(byte[])(to!(string)(i))); 65 | return this; 66 | } 67 | 68 | StringBuilder append(float f) { 69 | _buffer.put(cast(byte[])(to!(string)(f))); 70 | return this; 71 | } 72 | 73 | StringBuilder append(const(char)[] s) { 74 | _buffer.put(cast(byte[]) s); 75 | return this; 76 | } 77 | 78 | StringBuilder append(const(char)[] s, int start, int end) { 79 | _buffer.put(cast(byte[]) s[start .. end]); 80 | return this; 81 | } 82 | 83 | // StringBuilder append(byte[] s, int start, int end) 84 | // { 85 | // _buffer.put(s[start..end]); 86 | // return this; 87 | // } 88 | 89 | /// Warning: It's different from the previous one. 90 | StringBuilder append(byte[] str, int offset, int len) { 91 | _buffer.put(str[offset .. offset + len]); 92 | return this; 93 | } 94 | 95 | StringBuilder append(Object obj) { 96 | _buffer.put(cast(byte[])(obj.toString)); 97 | return this; 98 | } 99 | 100 | int length() { 101 | return cast(int) _buffer.data.length; 102 | } 103 | 104 | void setLength(int newLength) { 105 | _buffer.shrinkTo(newLength); 106 | // if (newLength < 0) 107 | // throw new StringIndexOutOfBoundsException(to!string(newLength)); 108 | // ensureCapacityInternal(newLength); 109 | 110 | // if (count < newLength) { 111 | // Arrays.fill(value, count, newLength, '\0'); 112 | // } 113 | 114 | // count = newLength; 115 | } 116 | 117 | private void ensureCapacityInternal(size_t minimumCapacity) { 118 | // overflow-conscious code 119 | // if (minimumCapacity > value.length) { 120 | // value = Arrays.copyOf(value, 121 | // newCapacity(minimumCapacity)); 122 | // } 123 | } 124 | 125 | int lastIndexOf(string s) { 126 | string source = cast(string) _buffer.data; 127 | return cast(int) source.lastIndexOf(s); 128 | } 129 | 130 | char charAt(int idx) { 131 | if (length() > idx) 132 | return _buffer.data[idx]; 133 | else 134 | return ' '; 135 | } 136 | 137 | StringBuilder deleteCharAt(int index) { 138 | if (index < length()) { 139 | auto data = _buffer.data.idup; 140 | for (int i = index + 1; i < data.length; i++) { 141 | _buffer.data[i - 1] = data[i]; 142 | } 143 | setLength(cast(int)(data.length - 1)); 144 | } 145 | return this; 146 | } 147 | 148 | StringBuilder insert(int index, char c) { 149 | if (index <= length()) { 150 | auto data = _buffer.data.idup; 151 | for (int i = index; i < data.length; i++) { 152 | _buffer.data[i + 1] = data[i]; 153 | } 154 | _buffer.data[index] = c; 155 | setLength(cast(int)(data.length + 1)); 156 | } 157 | return this; 158 | } 159 | 160 | StringBuilder insert(int index, long data) { 161 | auto bytes = cast(byte[])(to!string(data)); 162 | auto start = index; 163 | foreach (b; bytes) { 164 | insert(start, cast(char) b); 165 | start++; 166 | } 167 | return this; 168 | } 169 | 170 | StringBuilder replace(int start, int end, string str) { 171 | if (start <= end && start < length() && end < length()) { 172 | if (str.length >= end) 173 | _buffer.data[start .. end] = cast(byte[])(str[start .. end]); 174 | } 175 | return this; 176 | } 177 | 178 | void clear() { 179 | _buffer = Appender!(byte[]).init; 180 | } 181 | 182 | override string toString() { 183 | string s = cast(string) _buffer.data.idup; 184 | if (s is null) 185 | return ""; 186 | else 187 | return s; 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /source/hunt/util/ThreadHelper.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A refined core library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.util.ThreadHelper; 13 | 14 | import core.thread; 15 | 16 | version (Posix) { 17 | import hunt.system.syscall; 18 | 19 | ThreadID getTid() { 20 | version(FreeBSD) { 21 | long tid; 22 | syscall(SYS_thr_self, &tid); 23 | return cast(ThreadID)tid; 24 | } else version(OSX) { 25 | return cast(ThreadID)syscall(SYS_thread_selfid); 26 | } else version(linux) { 27 | return cast(ThreadID)syscall(__NR_gettid); 28 | } else { 29 | return 0; 30 | } 31 | } 32 | } else { 33 | import core.sys.windows.winbase: GetCurrentThreadId; 34 | ThreadID getTid() { 35 | return GetCurrentThreadId(); 36 | } 37 | } -------------------------------------------------------------------------------- /source/hunt/util/Timer.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A refined core library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.util.Timer; 13 | 14 | import hunt.event; 15 | import hunt.event.timer; 16 | import hunt.logging; 17 | import hunt.Exceptions; 18 | 19 | import core.time; 20 | 21 | /** 22 | * 23 | */ 24 | class Timer : AbstractTimer { 25 | 26 | this(Selector loop) { 27 | super(loop); 28 | this.interval = 1000; 29 | } 30 | 31 | this(Selector loop, size_t interval) { 32 | super(loop); 33 | this.interval = interval; 34 | } 35 | 36 | this(Selector loop, Duration duration) { 37 | super(loop); 38 | this.interval = duration; 39 | } 40 | 41 | protected: 42 | 43 | override void onRead() { 44 | bool canRead = true; 45 | while (canRead && _isRegistered) { 46 | canRead = readTimer((Object obj) { 47 | BaseTypeObject!uint tm = cast(BaseTypeObject!uint) obj; 48 | if (tm is null) 49 | return; 50 | while (tm.data > 0) { 51 | if (ticked !is null) 52 | ticked(this); 53 | tm.data--; 54 | } 55 | }); 56 | if (this.isError) { 57 | canRead = false; 58 | this.close(); 59 | error("the Timer Read is error: ", this.errorMessage); 60 | } 61 | } 62 | } 63 | 64 | } 65 | 66 | // dfmt off 67 | version (HAVE_IOCP) : 68 | // dfmt on 69 | 70 | import std.datetime; 71 | import std.exception; 72 | import std.process; 73 | 74 | import core.sys.windows.windows; 75 | import core.thread; 76 | import core.time; 77 | 78 | /** 79 | */ 80 | abstract class AbstractNativeTimer : ITimer { 81 | protected bool _isActive = false; 82 | protected size_t _interval = 1000; 83 | 84 | /// Timer tick handler 85 | TickedEventHandler ticked; 86 | 87 | this() { 88 | this(1000); 89 | } 90 | 91 | this(size_t interval) { 92 | this.interval = interval; 93 | } 94 | 95 | this(Duration duration) { 96 | this.interval = duration; 97 | } 98 | 99 | /// 100 | @property bool isActive() { 101 | return _isActive; 102 | } 103 | 104 | /// in ms 105 | @property size_t interval() { 106 | return _interval; 107 | } 108 | 109 | /// ditto 110 | @property ITimer interval(size_t v) { 111 | _interval = v; 112 | return this; 113 | } 114 | 115 | /// ditto 116 | @property ITimer interval(Duration duration) { 117 | _interval = cast(size_t) duration.total!("msecs"); 118 | return this; 119 | } 120 | 121 | /// The handler will be handled in another thread. 122 | ITimer onTick(TickedEventHandler handler) { 123 | this.ticked = handler; 124 | return this; 125 | } 126 | 127 | /// immediately: true to call first event immediately 128 | /// once: true to call timed event only once 129 | abstract void start(bool immediately = false, bool once = false); 130 | void start(uint interval) { 131 | this.interval = interval; 132 | start(); 133 | } 134 | 135 | abstract void stop(); 136 | 137 | abstract void reset(bool immediately = false, bool once = false); 138 | 139 | void reset(size_t interval) { 140 | this.interval = interval; 141 | reset(); 142 | } 143 | 144 | void reset(Duration duration) { 145 | this.interval = duration; 146 | reset(); 147 | } 148 | 149 | protected void onTick() { 150 | // trace("tick thread id: ", getTid()); 151 | if (ticked !is null) 152 | ticked(this); 153 | } 154 | } 155 | 156 | /** 157 | * See_also: 158 | * https://www.codeproject.com/articles/146617/simple-c-timer-wrapper 159 | * https://msdn.microsoft.com/en-us/library/ms687003(v=vs.85) 160 | */ 161 | class NativeTimer : AbstractNativeTimer { 162 | protected HANDLE _handle = null; 163 | 164 | this() { 165 | super(1000); 166 | } 167 | 168 | this(size_t interval) { 169 | super(interval); 170 | } 171 | 172 | this(Duration duration) { 173 | super(duration); 174 | } 175 | 176 | /// immediately: true to call first event immediately 177 | /// once: true to call timed event only once 178 | override void start(bool immediately = false, bool once = false) { 179 | version (HUNT_DEBUG) 180 | trace("main thread id: ", thisThreadID()); 181 | if (_isActive) 182 | return; 183 | BOOL r = CreateTimerQueueTimer(&_handle, null, &timerProc, 184 | cast(PVOID) this, immediately ? 0 : cast(int) interval, once ? 0 185 | : cast(int) interval, WT_EXECUTEINTIMERTHREAD); 186 | assert(r != 0); 187 | _isActive = true; 188 | } 189 | 190 | override void stop() { 191 | if (_isActive) { 192 | DeleteTimerQueueTimer(null, _handle, null); 193 | _isActive = false; 194 | } 195 | } 196 | 197 | override void reset(bool immediately = false, bool once = false) { 198 | if (_isActive) { 199 | assert(ChangeTimerQueueTimer(null, _handle, immediately ? 0 200 | : cast(int) interval, once ? 0 : cast(int) interval) != 0); 201 | } 202 | } 203 | 204 | /// https://msdn.microsoft.com/en-us/library/ms687066(v=vs.85) 205 | extern (Windows) static private void timerProc(PVOID param, bool timerCalled) { 206 | version (HUNT_DEBUG) 207 | trace("handler thread id: ", thisThreadID()); 208 | AbstractNativeTimer timer = cast(AbstractNativeTimer)(param); 209 | assert(timer !is null); 210 | timer.onTick(); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /source/hunt/util/Traits.d: -------------------------------------------------------------------------------- 1 | /* 2 | * Hunt - A refined core library for D programming language. 3 | * 4 | * Copyright (C) 2018-2019 HuntLabs 5 | * 6 | * Website: https://www.huntlabs.net/ 7 | * 8 | * Licensed under the Apache-2.0 License. 9 | * 10 | */ 11 | 12 | module hunt.util.Traits; 13 | 14 | import std.array; 15 | import std.meta; 16 | import std.typecons; 17 | import std.string; 18 | import std.traits; 19 | 20 | 21 | 22 | template isInheritClass(T, Base) { 23 | enum FFilter(U) = is(U == Base); 24 | enum isInheritClass = (Filter!(FFilter, BaseTypeTuple!T).length > 0); 25 | } 26 | 27 | template isByteType(T) { 28 | enum bool isByteType = is(T == byte) || is(T == ubyte) || is(T == char); 29 | } 30 | 31 | template isCharByte(T) { 32 | enum bool isCharByte = is(Unqual!T == byte) || is(Unqual!T == ubyte) || is(Unqual!T == char); 33 | } 34 | 35 | template isByteArray(T) { 36 | static if(is(T : U[], U) && isByteType!(Unqual!U)) { 37 | enum bool isByteArray = true; 38 | } else { 39 | enum bool isByteArray = false; 40 | } 41 | } 42 | 43 | 44 | template isRefType(T) { 45 | enum isRefType = /*isPointer!T ||*/ isDelegate!T || isDynamicArray!T 46 | || isAssociativeArray!T || is(T == class) || is(T == interface); 47 | } 48 | 49 | template isPublic(alias T) { 50 | enum isPublic = (__traits(getProtection, T) == "public"); 51 | } 52 | -------------------------------------------------------------------------------- /source/hunt/util/queue/Queue.d: -------------------------------------------------------------------------------- 1 | module hunt.util.queue.Queue; 2 | 3 | /** 4 | * 5 | */ 6 | abstract class Queue(T) { 7 | 8 | bool isEmpty(); 9 | 10 | T pop(); 11 | 12 | void push(T task); 13 | 14 | void clear() { 15 | // do nothing 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /source/hunt/util/queue/SimpleQueue.d: -------------------------------------------------------------------------------- 1 | module hunt.util.queue.SimpleQueue; 2 | 3 | import hunt.logging; 4 | import hunt.util.queue.Queue; 5 | 6 | import core.atomic; 7 | import core.sync.condition; 8 | import core.sync.mutex; 9 | import core.time; 10 | import core.thread; 11 | 12 | import std.container.dlist; 13 | 14 | 15 | /** 16 | * It's a thread-safe queue 17 | */ 18 | class SimpleQueue(T) : Queue!(T) { 19 | private DList!T _list; 20 | private Mutex _headLock; 21 | private Duration _timeout; 22 | private bool _isWaiting = false; 23 | 24 | shared int _incomings = 0; 25 | shared int _outgoings = 0; 26 | 27 | /** Wait queue for waiting takes */ 28 | private Condition _notEmpty; 29 | 30 | this(Duration timeout = 10.seconds) { 31 | _timeout = timeout; 32 | _headLock = new Mutex(); 33 | _notEmpty = new Condition(_headLock); 34 | } 35 | 36 | override bool isEmpty() { 37 | _headLock.lock(); 38 | scope (exit) 39 | _headLock.unlock(); 40 | 41 | return _list.empty(); 42 | } 43 | 44 | override T pop() { 45 | _headLock.lock(); 46 | scope (exit) { 47 | _headLock.unlock(); 48 | } 49 | 50 | if(isEmpty()) { 51 | _isWaiting = true; 52 | bool v = _notEmpty.wait(_timeout); 53 | _isWaiting = false; 54 | if(!v) { 55 | version (HUNT_IO_DEBUG) { 56 | infof("Timeout in %s.", _timeout); 57 | } 58 | return T.init; 59 | } 60 | } 61 | 62 | if(_list.empty()) 63 | return T.init; 64 | 65 | T item = _list.front(); 66 | _list.removeFront(); 67 | 68 | return item; 69 | } 70 | 71 | override void push(T item) { 72 | _headLock.lock(); 73 | scope (exit) 74 | _headLock.unlock(); 75 | 76 | _list.insert(item); 77 | 78 | if(_isWaiting) { 79 | _notEmpty.notify(); 80 | } 81 | } 82 | 83 | override void clear() { 84 | _headLock.lock(); 85 | scope (exit) 86 | _headLock.unlock(); 87 | 88 | _list.clear(); 89 | _notEmpty.notify(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /source/hunt/util/queue/package.d: -------------------------------------------------------------------------------- 1 | module hunt.util.queue; 2 | 3 | public import hunt.util.queue.Queue; 4 | public import hunt.util.queue.SimpleQueue; -------------------------------------------------------------------------------- /source/hunt/util/worker/Task.d: -------------------------------------------------------------------------------- 1 | module hunt.util.worker.Task; 2 | 3 | import hunt.util.queue; 4 | import hunt.logging; 5 | 6 | import core.atomic; 7 | import std.datetime; 8 | import std.format; 9 | 10 | enum TaskStatus : ubyte { 11 | Ready, 12 | Processing, 13 | Terminated, 14 | Done 15 | } 16 | 17 | alias TaskQueue = Queue!Task; 18 | alias MemoryTaskQueue = SimpleQueue!Task; 19 | 20 | /** 21 | * 22 | */ 23 | abstract class Task { 24 | protected shared TaskStatus _status; 25 | 26 | size_t id; 27 | 28 | private MonoTime _createTime; 29 | private MonoTime _startTime; 30 | private MonoTime _endTime; 31 | 32 | this() { 33 | _status = TaskStatus.Ready; 34 | _createTime = MonoTime.currTime; 35 | } 36 | 37 | Duration survivalTime() { 38 | return _endTime - _createTime; 39 | } 40 | 41 | Duration executionTime() { 42 | return _endTime - _startTime; 43 | } 44 | 45 | Duration lifeTime() { 46 | if(_endTime > _createTime) { 47 | return survivalTime(); 48 | } else { 49 | return MonoTime.currTime - _createTime; 50 | } 51 | } 52 | 53 | TaskStatus status() { 54 | return _status; 55 | } 56 | 57 | bool isReady() { 58 | return _status == TaskStatus.Ready; 59 | } 60 | 61 | bool isProcessing() { 62 | return _status == TaskStatus.Processing; 63 | } 64 | 65 | bool isTerminated() { 66 | return _status == TaskStatus.Terminated; 67 | } 68 | 69 | bool isDone() { 70 | return _status == TaskStatus.Done; 71 | } 72 | 73 | void stop() { 74 | 75 | version(HUNT_IO_DEBUG) { 76 | tracef("The task status: %s", _status); 77 | } 78 | 79 | if(!cas(&_status, TaskStatus.Processing, TaskStatus.Terminated) && 80 | !cas(&_status, TaskStatus.Ready, TaskStatus.Terminated)) { 81 | version(HUNT_IO_DEBUG) { 82 | warningf("The task status: %s", _status); 83 | } 84 | } 85 | } 86 | 87 | void finish() { 88 | version(HUNT_IO_DEBUG) { 89 | tracef("The task %d status: %s", id, _status); 90 | } 91 | 92 | if(cas(&_status, TaskStatus.Processing, TaskStatus.Done) || 93 | cas(&_status, TaskStatus.Ready, TaskStatus.Done)) { 94 | 95 | _endTime = MonoTime.currTime; 96 | version(HUNT_IO_DEBUG) { 97 | infof("The task %d done.", id); 98 | } 99 | } else { 100 | version(HUNT_IO_DEBUG) { 101 | warningf("The task %d status: %s", _status, id); 102 | warningf("Failed to set the task status to Done: %s", _status); 103 | } 104 | } 105 | } 106 | 107 | protected void doExecute(); 108 | 109 | void execute() { 110 | if(cas(&_status, TaskStatus.Ready, TaskStatus.Processing)) { 111 | version(HUNT_IO_DEBUG) { 112 | tracef("Task %d executing... status: %s", id, _status); 113 | } 114 | _startTime = MonoTime.currTime; 115 | scope(exit) { 116 | finish(); 117 | version(HUNT_IO_DEBUG) { 118 | infof("Task %d Done!", id); 119 | } 120 | } 121 | doExecute(); 122 | } else { 123 | warningf("Failed to execute task %d. Its status is: %s", id, _status); 124 | } 125 | } 126 | 127 | override string toString() { 128 | return format("id: %d, status: %s", id, _status); 129 | } 130 | 131 | } -------------------------------------------------------------------------------- /source/hunt/util/worker/Worker.d: -------------------------------------------------------------------------------- 1 | module hunt.util.worker.Worker; 2 | 3 | import hunt.util.worker.Task; 4 | // import hunt.util.worker.TaskQueue; 5 | import hunt.util.worker.WorkerThread; 6 | import hunt.logging; 7 | 8 | import core.atomic; 9 | import core.sync.condition; 10 | import core.sync.mutex; 11 | import core.thread; 12 | 13 | import std.conv; 14 | import std.concurrency; 15 | 16 | 17 | 18 | /** 19 | * 20 | */ 21 | class Worker { 22 | 23 | private size_t _size; 24 | private Thread _masterThread; 25 | private WorkerThread[] _workerThreads; 26 | private Task[size_t] _tasks; 27 | private Mutex _taskLocker; 28 | 29 | 30 | private TaskQueue _taskQueue; 31 | private shared bool _isRunning = false; 32 | 33 | this(TaskQueue taskQueue, size_t size = 8) { 34 | _taskQueue = taskQueue; 35 | _size = size; 36 | 37 | version(HUNT_DEBUG) { 38 | infof("Worker size: %d", size); 39 | } 40 | 41 | initialize(); 42 | } 43 | 44 | private void initialize() { 45 | _taskLocker = new Mutex(); 46 | _workerThreads = new WorkerThread[_size]; 47 | 48 | foreach(size_t index; 0 .. _size) { 49 | WorkerThread thread = new WorkerThread(index); 50 | thread.start(); 51 | 52 | _workerThreads[index] = thread; 53 | } 54 | } 55 | 56 | void inspect() { 57 | 58 | foreach(WorkerThread th; _workerThreads) { 59 | 60 | Task task = th.task(); 61 | 62 | if(th.state() == WorkerThreadState.Busy) { 63 | if(task is null) { 64 | warning("A dead worker thread detected: %s, %s", th.name, th.state()); 65 | } else { 66 | tracef("Thread: %s, state: %s, lifeTime: %s", th.name, th.state(), task.lifeTime()); 67 | } 68 | } else { 69 | if(task is null) { 70 | tracef("Thread: %s, state: %s", th.name, th.state()); 71 | } else { 72 | tracef("Thread: %s, state: %s", th.name, th.state(), task.executionTime); 73 | } 74 | } 75 | } 76 | } 77 | 78 | void put(Task task) { 79 | _taskQueue.push(task); 80 | 81 | _taskLocker.lock(); 82 | scope(exit) { 83 | _taskLocker.unlock(); 84 | } 85 | 86 | _tasks[task.id] = task; 87 | } 88 | 89 | Task get(size_t id) { 90 | _taskLocker.lock(); 91 | scope(exit) { 92 | _taskLocker.unlock(); 93 | } 94 | 95 | auto itemPtr = id in _tasks; 96 | if(itemPtr is null) { 97 | throw new Exception("Task does NOT exist: " ~ id.to!string); 98 | } 99 | 100 | return *itemPtr; 101 | } 102 | 103 | void remove(size_t id) { 104 | _taskLocker.lock(); 105 | scope(exit) { 106 | _taskLocker.unlock(); 107 | } 108 | 109 | _tasks.remove(id); 110 | } 111 | 112 | void clear() { 113 | _taskLocker.lock(); 114 | scope(exit) { 115 | _taskLocker.unlock(); 116 | } 117 | _tasks.clear(); 118 | 119 | } 120 | 121 | void run() { 122 | bool r = cas(&_isRunning, false, true); 123 | if(r) { 124 | _masterThread = new Thread(&doRun); 125 | _masterThread.start(); 126 | } 127 | } 128 | 129 | void stop() { 130 | bool r = cas(&_isRunning, true, false); 131 | 132 | if(r) { 133 | version(HUNT_IO_DEBUG) { 134 | info("Stopping all the threads..."); 135 | } 136 | 137 | 138 | foreach(size_t index; 0 .. _size) { 139 | _workerThreads[index].stop(); 140 | _workerThreads[index].join(); 141 | } 142 | 143 | 144 | // To stop the master thread as soon as possible. 145 | // _taskQueue.push(null); 146 | _taskQueue.clear(); 147 | 148 | if(_masterThread !is null) { 149 | _masterThread.join(); 150 | } 151 | 152 | version(HUNT_IO_DEBUG) { 153 | info("All the threads stopped."); 154 | } 155 | } 156 | } 157 | 158 | private WorkerThread findIdleThread() { 159 | foreach(size_t index, WorkerThread thread; _workerThreads) { 160 | version(HUNT_IO_DEBUG) { 161 | tracef("Thread: %s, state: %s", thread.name, thread.state); 162 | } 163 | 164 | if(thread.isIdle()) 165 | return thread; 166 | } 167 | 168 | return null; 169 | } 170 | 171 | private void doRun() { 172 | while(_isRunning) { 173 | try { 174 | version(HUNT_IO_DEBUG) info("running..."); 175 | Task task = _taskQueue.pop(); 176 | if(!_isRunning) break; 177 | 178 | if(task is null) { 179 | version(HUNT_IO_DEBUG) { 180 | warning("A null task popped!"); 181 | inspect(); 182 | } 183 | continue; 184 | } 185 | 186 | WorkerThread workerThread; 187 | bool isAttatched = false; 188 | 189 | do { 190 | workerThread = findIdleThread(); 191 | 192 | // All worker threads are busy! 193 | if(workerThread is null) { 194 | // version(HUNT_METRIC) { 195 | // _taskQueue.inspect(); 196 | // } 197 | // trace("All worker threads are busy!"); 198 | // Thread.sleep(1.seconds); 199 | // Thread.sleep(10.msecs); 200 | Thread.yield(); 201 | } else { 202 | isAttatched = workerThread.attatch(task); 203 | } 204 | } while(!isAttatched && _isRunning); 205 | 206 | } catch(Throwable ex) { 207 | warning(ex); 208 | } 209 | } 210 | 211 | version(HUNT_IO_DEBUG) info("Worker stopped!"); 212 | 213 | } 214 | 215 | } 216 | 217 | -------------------------------------------------------------------------------- /source/hunt/util/worker/WorkerThread.d: -------------------------------------------------------------------------------- 1 | module hunt.util.worker.WorkerThread; 2 | 3 | import hunt.util.Closeable; 4 | import hunt.util.ResoureManager; 5 | import hunt.util.worker.Task; 6 | import hunt.util.worker.Worker; 7 | 8 | import hunt.logging; 9 | 10 | import core.atomic; 11 | import core.memory; 12 | import core.thread; 13 | import core.sync.condition; 14 | import core.sync.mutex; 15 | import std.conv; 16 | 17 | 18 | 19 | enum WorkerThreadState { 20 | Idle, 21 | Busy, // occupied 22 | Stopped 23 | } 24 | 25 | bool inWorkerThread() { 26 | WorkerThread th = cast(WorkerThread) Thread.getThis(); 27 | return th !is null; 28 | } 29 | 30 | /** 31 | * 32 | */ 33 | class WorkerThread : Thread { 34 | 35 | private shared WorkerThreadState _state; 36 | private size_t _index; 37 | private Task _task; 38 | private Duration _timeout; 39 | 40 | private Condition _condition; 41 | private Mutex _mutex; 42 | 43 | this(size_t index, Duration timeout = 5.seconds, size_t stackSize = 0) { 44 | _index = index; 45 | _timeout = timeout; 46 | _state = WorkerThreadState.Idle; 47 | _mutex = new Mutex(); 48 | _condition = new Condition(_mutex); 49 | this.name = "WorkerThread-" ~ _index.to!string(); 50 | super(&run, stackSize); 51 | } 52 | 53 | void stop() { 54 | version(HUNT_IO_DEBUG) { 55 | infof("Stopping thread %s", this.name); 56 | } 57 | _state = WorkerThreadState.Stopped; 58 | 59 | _mutex.lock(); 60 | scope (exit) { 61 | _mutex.unlock(); 62 | } 63 | _condition.notify(); 64 | } 65 | 66 | bool isBusy() { 67 | return _state == WorkerThreadState.Busy; 68 | } 69 | 70 | bool isIdle() { 71 | return _state == WorkerThreadState.Idle; 72 | } 73 | 74 | WorkerThreadState state() { 75 | return _state; 76 | } 77 | 78 | size_t index() { 79 | return _index; 80 | } 81 | 82 | Task task() { 83 | return _task; 84 | } 85 | 86 | bool attatch(Task task) { 87 | assert(task !is null); 88 | bool r = cas(&_state, WorkerThreadState.Idle, WorkerThreadState.Busy); 89 | 90 | if (r) { 91 | version(HUNT_IO_DEBUG) { 92 | infof("attatching task %d with thread %s", task.id, this.name); 93 | } 94 | 95 | _mutex.lock(); 96 | scope (exit) { 97 | _mutex.unlock(); 98 | } 99 | _task = task; 100 | _condition.notify(); 101 | 102 | } else { 103 | warningf("%s is unavailable. state: %s", this.name(), _state); 104 | } 105 | 106 | return r; 107 | } 108 | 109 | private void run() nothrow { 110 | while (_state != WorkerThreadState.Stopped) { 111 | 112 | scope (exit) { 113 | version (HUNT_IO_DEBUG) { 114 | tracef("%s Done. state: %s", this.name(), _state); 115 | } 116 | 117 | collectResoure(); 118 | _task = null; 119 | 120 | if(_state != WorkerThreadState.Stopped) { 121 | bool r = cas(&_state, WorkerThreadState.Busy, WorkerThreadState.Idle); 122 | version(HUNT_IO_DEBUG) { 123 | if(!r) { 124 | warningf("Failed to set thread %s to Idle, its state is %s", this.name, _state); 125 | } 126 | } 127 | } 128 | } 129 | 130 | try { 131 | doRun(); 132 | } catch (Throwable ex) { 133 | warning(ex); 134 | } 135 | } 136 | 137 | version (HUNT_DEBUG) tracef("%s Stopped. state: %s", this.name(), _state); 138 | } 139 | 140 | private bool _isWaiting = false; 141 | 142 | private void doRun() { 143 | _mutex.lock(); 144 | 145 | Task task = _task; 146 | while(task is null && _state != WorkerThreadState.Stopped) { 147 | bool r = _condition.wait(_timeout); 148 | task = _task; 149 | 150 | version(HUNT_IO_DEBUG) { 151 | if(!r && _state == WorkerThreadState.Busy) { 152 | if(task is null) { 153 | warningf("No task attatched on a busy thread %s in %s, task: %s", this.name, _timeout); 154 | } else { 155 | warningf("more tests need for this status, thread %s in %s", this.name, _timeout); 156 | } 157 | } 158 | } 159 | } 160 | 161 | _mutex.unlock(); 162 | 163 | if(task !is null) { 164 | version(HUNT_IO_DEBUG) { 165 | tracef("Try to exeucte task %d in thread %s, its status: %s", task.id, this.name, task.status); 166 | } 167 | task.execute(); 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /source/hunt/util/worker/package.d: -------------------------------------------------------------------------------- 1 | module hunt.util.worker; 2 | 3 | public import hunt.util.worker.Task; 4 | public import hunt.util.worker.Worker; 5 | public import hunt.util.worker.WorkerThread; --------------------------------------------------------------------------------