├── .circleci ├── config.yml └── scripts │ ├── build_spdk.sh │ └── run_test.sh ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── rust-toolchain ├── rustfmt.toml ├── spdk-sys ├── Cargo.toml ├── build.rs ├── config │ └── hello_blob.conf └── src │ ├── bdev.rs │ ├── blob.rs │ ├── blob_bdev.rs │ ├── env.rs │ ├── event.rs │ ├── generated │ └── mod.rs │ ├── io_channel.rs │ └── lib.rs ├── src └── lib.rs ├── starfish-example-app ├── Cargo.toml ├── config │ └── hello_blob.conf └── src │ └── main.rs └── starfish-executor ├── Cargo.toml └── src ├── lib.rs ├── waker.rs └── waker_ref.rs /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | machine: 5 | image: circleci/classic:201710-01 6 | steps: 7 | - checkout 8 | - run: 9 | name: Build spdk 10 | command: sh .circleci/scripts/build_spdk.sh 11 | - run: 12 | name: Install rustup 13 | command: | 14 | curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain none -y 15 | - restore_cache: 16 | keys: 17 | - deps-${CACHE_VERSION}-{{ .Branch }}-{{ .Revision }} 18 | - deps-${CACHE_VERSION}-{{ .Branch }}- 19 | - deps-${CACHE_VERSION}- 20 | - run: 21 | name: Cache dependencies 22 | command: | 23 | source $HOME/.cargo/env 24 | cargo fetch 25 | - save_cache: 26 | key: deps-${CACHE_VERSION}-{{ .Branch }}-{{ .Revision }} 27 | paths: 28 | - "target" 29 | - run: 30 | name: Run 'cargo fmt' 31 | command: | 32 | source $HOME/.cargo/env 33 | rustup component add rustfmt-preview 34 | cargo fmt --all -- --check 35 | - run: 36 | name: Run 'cargo clippy' 37 | command: | 38 | source $HOME/.cargo/env 39 | rustup component add clippy-preview 40 | cargo clippy --all --all-targets --all-features -- -D warnings 41 | - run: 42 | name: Run tests 43 | command: | 44 | sudo bash .circleci/scripts/run_test.sh 45 | -------------------------------------------------------------------------------- /.circleci/scripts/build_spdk.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd /tmp 4 | git clone git@github.com:spdk/spdk.git 5 | 6 | cd /tmp/spdk 7 | git checkout v18.07.1 8 | git submodule update --init 9 | 10 | echo 'APT::Get::Assume-Yes "true"; APT::Get::force-yes "true";' > /etc/apt/apt.conf.d/99force-yes 11 | sudo apt-get update 12 | sudo ./scripts/pkgdep.sh 13 | 14 | sudo apt-get install -y module-init-tools 15 | 16 | sudo /tmp/spdk/scripts/setup.sh 17 | 18 | if [ ! -f "/usr/local/lib/libspdk.so" ]; then 19 | ./configure 20 | sudo make install 21 | else 22 | echo "spdk already built" 23 | fi 24 | -------------------------------------------------------------------------------- /.circleci/scripts/run_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain none -y 4 | source $HOME/.cargo/env 5 | dd if=/dev/zero of=/tmp/aiofile bs=2048 count=5000 6 | export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:/usr/local/lib" 7 | cd /home/circleci/project 8 | RUST_BACKTRACE=trace cargo test --all -- --nocapture -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Rust template 2 | # Generated by Cargo 3 | # will have compiled files and executables 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 8 | # Cargo.lock 9 | #/.cargo/ 10 | 11 | # IDE directories 12 | /.idea/ 13 | /.vscode/ 14 | /*.code-workspace -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "aho-corasick" 5 | version = "0.7.6" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | dependencies = [ 8 | "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 9 | ] 10 | 11 | [[package]] 12 | name = "ansi_term" 13 | version = "0.11.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | dependencies = [ 16 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 17 | ] 18 | 19 | [[package]] 20 | name = "atty" 21 | version = "0.2.13" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | dependencies = [ 24 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 25 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 26 | ] 27 | 28 | [[package]] 29 | name = "autocfg" 30 | version = "0.1.5" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | 33 | [[package]] 34 | name = "backtrace" 35 | version = "0.3.11" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | dependencies = [ 38 | "backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", 39 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 40 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 41 | "rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", 42 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 43 | ] 44 | 45 | [[package]] 46 | name = "backtrace-sys" 47 | version = "0.1.31" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | dependencies = [ 50 | "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", 51 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 52 | ] 53 | 54 | [[package]] 55 | name = "bindgen" 56 | version = "0.41.0" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | dependencies = [ 59 | "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 60 | "cexpr 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 61 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 62 | "clang-sys 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)", 63 | "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", 64 | "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", 65 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 66 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 67 | "peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 68 | "proc-macro2 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 69 | "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", 70 | "regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 71 | "which 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 72 | ] 73 | 74 | [[package]] 75 | name = "bitflags" 76 | version = "1.1.0" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | 79 | [[package]] 80 | name = "cc" 81 | version = "1.0.38" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | 84 | [[package]] 85 | name = "cexpr" 86 | version = "0.2.3" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | dependencies = [ 89 | "nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 90 | ] 91 | 92 | [[package]] 93 | name = "cfg-if" 94 | version = "0.1.9" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | 97 | [[package]] 98 | name = "chrono" 99 | version = "0.4.7" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | dependencies = [ 102 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 103 | "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", 104 | "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 105 | "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", 106 | ] 107 | 108 | [[package]] 109 | name = "clang-sys" 110 | version = "0.23.0" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | dependencies = [ 113 | "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 114 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 115 | "libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", 116 | ] 117 | 118 | [[package]] 119 | name = "clap" 120 | version = "2.33.0" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | dependencies = [ 123 | "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 124 | "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", 125 | "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 126 | "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 127 | "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 128 | "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 129 | "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", 130 | ] 131 | 132 | [[package]] 133 | name = "env_logger" 134 | version = "0.5.13" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | dependencies = [ 137 | "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", 138 | "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 139 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 140 | "regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 141 | "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 142 | ] 143 | 144 | [[package]] 145 | name = "err-derive" 146 | version = "0.1.5" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | dependencies = [ 149 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 150 | "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", 151 | "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 152 | "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", 153 | "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", 154 | ] 155 | 156 | [[package]] 157 | name = "failure" 158 | version = "0.1.2" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | dependencies = [ 161 | "backtrace 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", 162 | "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 163 | ] 164 | 165 | [[package]] 166 | name = "failure_derive" 167 | version = "0.1.5" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | dependencies = [ 170 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 171 | "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", 172 | "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", 173 | "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", 174 | ] 175 | 176 | [[package]] 177 | name = "fuchsia-cprng" 178 | version = "0.1.1" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | 181 | [[package]] 182 | name = "futures-channel-preview" 183 | version = "0.3.0-alpha.17" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | dependencies = [ 186 | "futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", 187 | "futures-sink-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", 188 | ] 189 | 190 | [[package]] 191 | name = "futures-core-preview" 192 | version = "0.3.0-alpha.17" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | 195 | [[package]] 196 | name = "futures-executor-preview" 197 | version = "0.3.0-alpha.17" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | dependencies = [ 200 | "futures-channel-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", 201 | "futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", 202 | "futures-util-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", 203 | "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", 204 | ] 205 | 206 | [[package]] 207 | name = "futures-io-preview" 208 | version = "0.3.0-alpha.17" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | 211 | [[package]] 212 | name = "futures-preview" 213 | version = "0.3.0-alpha.17" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | dependencies = [ 216 | "futures-channel-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", 217 | "futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", 218 | "futures-executor-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", 219 | "futures-io-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", 220 | "futures-sink-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", 221 | "futures-util-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", 222 | ] 223 | 224 | [[package]] 225 | name = "futures-sink-preview" 226 | version = "0.3.0-alpha.17" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | dependencies = [ 229 | "futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", 230 | ] 231 | 232 | [[package]] 233 | name = "futures-util-preview" 234 | version = "0.3.0-alpha.17" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | dependencies = [ 237 | "futures-channel-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", 238 | "futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", 239 | "futures-io-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", 240 | "futures-sink-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", 241 | "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 242 | "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", 243 | "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 244 | ] 245 | 246 | [[package]] 247 | name = "glob" 248 | version = "0.2.11" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | 251 | [[package]] 252 | name = "hamcrest2" 253 | version = "0.2.6" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | dependencies = [ 256 | "num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", 257 | "regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 258 | ] 259 | 260 | [[package]] 261 | name = "humantime" 262 | version = "1.2.0" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | dependencies = [ 265 | "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 266 | ] 267 | 268 | [[package]] 269 | name = "isatty" 270 | version = "0.1.9" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | dependencies = [ 273 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 274 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 275 | "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", 276 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 277 | ] 278 | 279 | [[package]] 280 | name = "kernel32-sys" 281 | version = "0.2.2" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | dependencies = [ 284 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 285 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 286 | ] 287 | 288 | [[package]] 289 | name = "lazy_static" 290 | version = "1.3.0" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | 293 | [[package]] 294 | name = "libc" 295 | version = "0.2.43" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | 298 | [[package]] 299 | name = "libloading" 300 | version = "0.5.2" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | dependencies = [ 303 | "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", 304 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 305 | ] 306 | 307 | [[package]] 308 | name = "log" 309 | version = "0.4.8" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | dependencies = [ 312 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 313 | ] 314 | 315 | [[package]] 316 | name = "memchr" 317 | version = "1.0.2" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | dependencies = [ 320 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 321 | ] 322 | 323 | [[package]] 324 | name = "memchr" 325 | version = "2.2.1" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | 328 | [[package]] 329 | name = "nom" 330 | version = "3.2.1" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | dependencies = [ 333 | "memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 334 | ] 335 | 336 | [[package]] 337 | name = "num" 338 | version = "0.1.42" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | dependencies = [ 341 | "num-bigint 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", 342 | "num-complex 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", 343 | "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", 344 | "num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", 345 | "num-rational 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", 346 | "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 347 | ] 348 | 349 | [[package]] 350 | name = "num-bigint" 351 | version = "0.1.44" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | dependencies = [ 354 | "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", 355 | "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 356 | "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 357 | "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", 358 | ] 359 | 360 | [[package]] 361 | name = "num-complex" 362 | version = "0.1.43" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | dependencies = [ 365 | "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 366 | "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", 367 | ] 368 | 369 | [[package]] 370 | name = "num-integer" 371 | version = "0.1.41" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | dependencies = [ 374 | "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 375 | "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 376 | ] 377 | 378 | [[package]] 379 | name = "num-iter" 380 | version = "0.1.39" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | dependencies = [ 383 | "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 384 | "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", 385 | "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 386 | ] 387 | 388 | [[package]] 389 | name = "num-rational" 390 | version = "0.1.42" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | dependencies = [ 393 | "num-bigint 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", 394 | "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", 395 | "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 396 | "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", 397 | ] 398 | 399 | [[package]] 400 | name = "num-traits" 401 | version = "0.2.8" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | dependencies = [ 404 | "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 405 | ] 406 | 407 | [[package]] 408 | name = "num_cpus" 409 | version = "1.10.1" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | dependencies = [ 412 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 413 | ] 414 | 415 | [[package]] 416 | name = "peeking_take_while" 417 | version = "0.1.2" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | 420 | [[package]] 421 | name = "pin-utils" 422 | version = "0.1.0-alpha.4" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | 425 | [[package]] 426 | name = "proc-macro2" 427 | version = "0.3.5" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | dependencies = [ 430 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 431 | ] 432 | 433 | [[package]] 434 | name = "proc-macro2" 435 | version = "0.4.30" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | dependencies = [ 438 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 439 | ] 440 | 441 | [[package]] 442 | name = "quick-error" 443 | version = "1.2.2" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | 446 | [[package]] 447 | name = "quote" 448 | version = "0.5.2" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | dependencies = [ 451 | "proc-macro2 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 452 | ] 453 | 454 | [[package]] 455 | name = "quote" 456 | version = "0.6.13" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | dependencies = [ 459 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 460 | ] 461 | 462 | [[package]] 463 | name = "rand" 464 | version = "0.4.6" 465 | source = "registry+https://github.com/rust-lang/crates.io-index" 466 | dependencies = [ 467 | "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 468 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 469 | "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 470 | "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 471 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 472 | ] 473 | 474 | [[package]] 475 | name = "rand_core" 476 | version = "0.3.1" 477 | source = "registry+https://github.com/rust-lang/crates.io-index" 478 | dependencies = [ 479 | "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 480 | ] 481 | 482 | [[package]] 483 | name = "rand_core" 484 | version = "0.4.2" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | 487 | [[package]] 488 | name = "rdrand" 489 | version = "0.4.0" 490 | source = "registry+https://github.com/rust-lang/crates.io-index" 491 | dependencies = [ 492 | "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 493 | ] 494 | 495 | [[package]] 496 | name = "redox_syscall" 497 | version = "0.1.56" 498 | source = "registry+https://github.com/rust-lang/crates.io-index" 499 | 500 | [[package]] 501 | name = "regex" 502 | version = "1.2.1" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | dependencies = [ 505 | "aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", 506 | "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 507 | "regex-syntax 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 508 | "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 509 | ] 510 | 511 | [[package]] 512 | name = "regex-syntax" 513 | version = "0.6.11" 514 | source = "registry+https://github.com/rust-lang/crates.io-index" 515 | 516 | [[package]] 517 | name = "rustc-demangle" 518 | version = "0.1.15" 519 | source = "registry+https://github.com/rust-lang/crates.io-index" 520 | 521 | [[package]] 522 | name = "rustc-serialize" 523 | version = "0.3.24" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | 526 | [[package]] 527 | name = "rustc_version" 528 | version = "0.2.3" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | dependencies = [ 531 | "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 532 | ] 533 | 534 | [[package]] 535 | name = "semver" 536 | version = "0.9.0" 537 | source = "registry+https://github.com/rust-lang/crates.io-index" 538 | dependencies = [ 539 | "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 540 | ] 541 | 542 | [[package]] 543 | name = "semver-parser" 544 | version = "0.7.0" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | 547 | [[package]] 548 | name = "slab" 549 | version = "0.4.2" 550 | source = "registry+https://github.com/rust-lang/crates.io-index" 551 | 552 | [[package]] 553 | name = "slog" 554 | version = "2.5.2" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | 557 | [[package]] 558 | name = "slog-term" 559 | version = "2.0.2" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | dependencies = [ 562 | "chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", 563 | "isatty 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 564 | "slog 2.5.2 (registry+https://github.com/rust-lang/crates.io-index)", 565 | "term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 566 | "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 567 | ] 568 | 569 | [[package]] 570 | name = "spdk-sys" 571 | version = "0.1.0" 572 | dependencies = [ 573 | "bindgen 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)", 574 | "err-derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 575 | "futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", 576 | "hamcrest2 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 577 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 578 | "starfish-executor 0.1.0", 579 | ] 580 | 581 | [[package]] 582 | name = "spectral" 583 | version = "0.6.0" 584 | source = "registry+https://github.com/rust-lang/crates.io-index" 585 | dependencies = [ 586 | "num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", 587 | ] 588 | 589 | [[package]] 590 | name = "starfish" 591 | version = "0.1.0" 592 | dependencies = [ 593 | "failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 594 | "futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", 595 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 596 | "slog-term 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 597 | "spectral 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", 598 | ] 599 | 600 | [[package]] 601 | name = "starfish-example-app" 602 | version = "0.1.0" 603 | dependencies = [ 604 | "failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 605 | "futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", 606 | "spdk-sys 0.1.0", 607 | "starfish-executor 0.1.0", 608 | ] 609 | 610 | [[package]] 611 | name = "starfish-executor" 612 | version = "0.1.0" 613 | dependencies = [ 614 | "futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", 615 | "hamcrest2 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 616 | ] 617 | 618 | [[package]] 619 | name = "strsim" 620 | version = "0.8.0" 621 | source = "registry+https://github.com/rust-lang/crates.io-index" 622 | 623 | [[package]] 624 | name = "syn" 625 | version = "0.15.42" 626 | source = "registry+https://github.com/rust-lang/crates.io-index" 627 | dependencies = [ 628 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 629 | "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", 630 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 631 | ] 632 | 633 | [[package]] 634 | name = "synstructure" 635 | version = "0.10.2" 636 | source = "registry+https://github.com/rust-lang/crates.io-index" 637 | dependencies = [ 638 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 639 | "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", 640 | "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", 641 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 642 | ] 643 | 644 | [[package]] 645 | name = "term" 646 | version = "0.4.6" 647 | source = "registry+https://github.com/rust-lang/crates.io-index" 648 | dependencies = [ 649 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 650 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 651 | ] 652 | 653 | [[package]] 654 | name = "termcolor" 655 | version = "1.0.5" 656 | source = "registry+https://github.com/rust-lang/crates.io-index" 657 | dependencies = [ 658 | "wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 659 | ] 660 | 661 | [[package]] 662 | name = "textwrap" 663 | version = "0.11.0" 664 | source = "registry+https://github.com/rust-lang/crates.io-index" 665 | dependencies = [ 666 | "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 667 | ] 668 | 669 | [[package]] 670 | name = "thread_local" 671 | version = "0.3.6" 672 | source = "registry+https://github.com/rust-lang/crates.io-index" 673 | dependencies = [ 674 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 675 | ] 676 | 677 | [[package]] 678 | name = "time" 679 | version = "0.1.42" 680 | source = "registry+https://github.com/rust-lang/crates.io-index" 681 | dependencies = [ 682 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 683 | "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", 684 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 685 | ] 686 | 687 | [[package]] 688 | name = "unicode-width" 689 | version = "0.1.5" 690 | source = "registry+https://github.com/rust-lang/crates.io-index" 691 | 692 | [[package]] 693 | name = "unicode-xid" 694 | version = "0.1.0" 695 | source = "registry+https://github.com/rust-lang/crates.io-index" 696 | 697 | [[package]] 698 | name = "vec_map" 699 | version = "0.8.1" 700 | source = "registry+https://github.com/rust-lang/crates.io-index" 701 | 702 | [[package]] 703 | name = "which" 704 | version = "1.0.5" 705 | source = "registry+https://github.com/rust-lang/crates.io-index" 706 | dependencies = [ 707 | "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", 708 | ] 709 | 710 | [[package]] 711 | name = "winapi" 712 | version = "0.2.8" 713 | source = "registry+https://github.com/rust-lang/crates.io-index" 714 | 715 | [[package]] 716 | name = "winapi" 717 | version = "0.3.7" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | dependencies = [ 720 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 721 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 722 | ] 723 | 724 | [[package]] 725 | name = "winapi-build" 726 | version = "0.1.1" 727 | source = "registry+https://github.com/rust-lang/crates.io-index" 728 | 729 | [[package]] 730 | name = "winapi-i686-pc-windows-gnu" 731 | version = "0.4.0" 732 | source = "registry+https://github.com/rust-lang/crates.io-index" 733 | 734 | [[package]] 735 | name = "winapi-util" 736 | version = "0.1.2" 737 | source = "registry+https://github.com/rust-lang/crates.io-index" 738 | dependencies = [ 739 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 740 | ] 741 | 742 | [[package]] 743 | name = "winapi-x86_64-pc-windows-gnu" 744 | version = "0.4.0" 745 | source = "registry+https://github.com/rust-lang/crates.io-index" 746 | 747 | [[package]] 748 | name = "wincolor" 749 | version = "1.0.1" 750 | source = "registry+https://github.com/rust-lang/crates.io-index" 751 | dependencies = [ 752 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 753 | "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 754 | ] 755 | 756 | [metadata] 757 | "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" 758 | "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 759 | "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" 760 | "checksum autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "22130e92352b948e7e82a49cdb0aa94f2211761117f29e052dd397c1ac33542b" 761 | "checksum backtrace 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "18b65ea1161bfb2dd6da6fade5edd4dbd08fba85012123dd333d2fd1b90b2782" 762 | "checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b" 763 | "checksum bindgen 0.41.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e64eeb92e16bd21d6c95738458f80b2c95bad56cc8eeef7fdfaaf70268c56fe3" 764 | "checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" 765 | "checksum cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)" = "ce400c638d48ee0e9ab75aef7997609ec57367ccfe1463f21bf53c3eca67bf46" 766 | "checksum cexpr 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42aac45e9567d97474a834efdee3081b3c942b2205be932092f53354ce503d6c" 767 | "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" 768 | "checksum chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "77d81f58b7301084de3b958691458a53c3f7e0b1d702f77e550b6a88e3a88abe" 769 | "checksum clang-sys 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d7f7c04e52c35222fffcc3a115b5daf5f7e2bfb71c13c4e2321afe1fc71859c2" 770 | "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" 771 | "checksum env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)" = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" 772 | "checksum err-derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3d8ff65eb6c2fc68e76557239d16f5698fd56603925b89856d3f0f7105fd4543" 773 | "checksum failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7efb22686e4a466b1ec1a15c2898f91fa9cb340452496dca654032de20ff95b9" 774 | "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" 775 | "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 776 | "checksum futures-channel-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "21c71ed547606de08e9ae744bb3c6d80f5627527ef31ecf2a7210d0e67bc8fae" 777 | "checksum futures-core-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "4b141ccf9b7601ef987f36f1c0d9522f76df3bba1cf2e63bfacccc044c4558f5" 778 | "checksum futures-executor-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "87ba260fe51080ba37f063ad5b0732c4ff1f737ea18dcb67833d282cdc2c6f14" 779 | "checksum futures-io-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "082e402605fcb8b1ae1e5ba7d7fdfd3e31ef510e2a8367dd92927bb41ae41b3a" 780 | "checksum futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "bf25f91c8a9a1f64c451e91b43ba269ed359b9f52d35ed4b3ce3f9c842435867" 781 | "checksum futures-sink-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "4309a25a1069a1f3c10647b227b9afe6722b67a030d3f00a9cbdc171fc038de4" 782 | "checksum futures-util-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)" = "af8198c48b222f02326940ce2b3aa9e6e91a32886eeaad7ca3b8e4c70daa3f4e" 783 | "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" 784 | "checksum hamcrest2 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9b007b926b513e7355b22ceb9b14f4062d891398259a8d3feed5b004370fed" 785 | "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" 786 | "checksum isatty 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e31a8281fc93ec9693494da65fbf28c0c2aa60a2eaec25dc58e2f31952e95edc" 787 | "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 788 | "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" 789 | "checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" 790 | "checksum libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" 791 | "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" 792 | "checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a" 793 | "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" 794 | "checksum nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05aec50c70fd288702bcd93284a8444607f3292dbdf2a30de5ea5dcdbe72287b" 795 | "checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" 796 | "checksum num-bigint 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "e63899ad0da84ce718c14936262a41cee2c79c981fc0a0e7c7beb47d5a07e8c1" 797 | "checksum num-complex 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "b288631d7878aaf59442cffd36910ea604ecd7745c36054328595114001c9656" 798 | "checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" 799 | "checksum num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "76bd5272412d173d6bf9afdf98db8612bbabc9a7a830b7bfc9c188911716132e" 800 | "checksum num-rational 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "ee314c74bd753fc86b4780aa9475da469155f3848473a261d2d18e35245a784e" 801 | "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" 802 | "checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" 803 | "checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" 804 | "checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" 805 | "checksum proc-macro2 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "77997c53ae6edd6d187fec07ec41b207063b5ee6f33680e9fa86d405cdd313d4" 806 | "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" 807 | "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" 808 | "checksum quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9949cfe66888ffe1d53e6ec9d9f3b70714083854be20fd5e271b232a017401e8" 809 | "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" 810 | "checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" 811 | "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" 812 | "checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" 813 | "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" 814 | "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" 815 | "checksum regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88c3d9193984285d544df4a30c23a4e62ead42edf70a4452ceb76dac1ce05c26" 816 | "checksum regex-syntax 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b143cceb2ca5e56d5671988ef8b15615733e7ee16cd348e064333b251b89343f" 817 | "checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af" 818 | "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" 819 | "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 820 | "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 821 | "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 822 | "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 823 | "checksum slog 2.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1cc9c640a4adbfbcc11ffb95efe5aa7af7309e002adab54b185507dbf2377b99" 824 | "checksum slog-term 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d8734c9ff9a45be6026599e15aad5c29c2beb35113488b6945391853a011690" 825 | "checksum spectral 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ae3c15181f4b14e52eeaac3efaeec4d2764716ce9c86da0c934c3e318649c5ba" 826 | "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 827 | "checksum syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)" = "eadc09306ca51a40555dd6fc2b415538e9e18bc9f870e47b1a524a79fe2dcf5e" 828 | "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" 829 | "checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1" 830 | "checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e" 831 | "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 832 | "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" 833 | "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" 834 | "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" 835 | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 836 | "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" 837 | "checksum which 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e84a603e7e0b1ce1aa1ee2b109c7be00155ce52df5081590d1ffb93f4f515cb2" 838 | "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 839 | "checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" 840 | "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 841 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 842 | "checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" 843 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 844 | "checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba" 845 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "starfish" 3 | version = "0.1.0" 4 | authors = ["Jakub Kozlowski "] 5 | license = "MIT/Apache-2.0" 6 | description = """ 7 | Async programming with spdk for rust. 8 | """ 9 | edition = '2018' 10 | 11 | [dependencies] 12 | failure = "= 0.1.2" 13 | futures-preview = "= 0.3.0-alpha.17" 14 | libc = "= 0.2.43" 15 | 16 | [dev-dependencies] 17 | spectral = "= 0.6.0" 18 | slog-term = "=2.0.2" 19 | 20 | [workspace] 21 | 22 | members = [ 23 | "spdk-sys", 24 | "starfish-executor", 25 | "starfish-example-app" 26 | ] 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # starfish 2 | 3 | [![CircleCI](https://circleci.com/gh/jkozlowski/starfish.svg?style=svg)](https://circleci.com/gh/jkozlowski/starfish) 4 | 5 | Async programming with spdk for rust (Linux only!). 6 | 7 | ## Building and Running 8 | 9 | ``` 10 | # First need to build spdk 11 | $ cd /tmp 12 | $ git clone git@github.com:spdk/spdk.git 13 | 14 | $ cd /tmp/spdk 15 | $ git checkout v18.07.1 16 | $ git submodule update --init 17 | $ sudo ./scripts/pkgdep.sh 18 | 19 | $ ./configure 20 | $ sudo make install 21 | $ ./scripts/setup.sh 22 | 23 | # Used for aio backed testing 24 | $ dd if=/dev/zero of=/tmp/aiofile bs=2048 count=5000 25 | 26 | $ sudo ldconfig /usr/local/lib 27 | 28 | # Need to run dpdk applications as root :( 29 | $ cargo build && sudo target/debug/starfish-example-app starfish-example-app/config/hello_blob.conf 30 | ``` 31 | 32 | ## Example apps 33 | 34 | * https://github.com/percona/tokudb-engine 35 | * https://github.com/percona/tokudb-engine/wiki/Write-optimized-fractal-tree-storage 36 | * https://github.com/Tokutek 37 | * https://www.percona.com/blog/wp-content/uploads/2011/11/how-fractal-trees-work.pdf 38 | 39 | ## Futures and async/await 40 | 41 | - https://internals.rust-lang.org/t/explicit-future-construction-implicit-await/7344 42 | - https://internals.rust-lang.org/t/pre-rfc-cps-transform-for-generators/7120 43 | 44 | ## Useful 45 | 46 | - https://github.com/japaric/xargo/issues/45 47 | - https://github.com/hnes/libaco 48 | - http://www.f-stack.org/ 49 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly-2019-08-01 -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2018" -------------------------------------------------------------------------------- /spdk-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "spdk-sys" 3 | version = "0.1.0" 4 | authors = ["Jakub Kozlowski "] 5 | categories = ["external-ffi-bindings"] 6 | build = "build.rs" 7 | description = """ 8 | Rust bindings to spdk. 9 | """ 10 | edition = '2018' 11 | 12 | links = "spdk" 13 | 14 | [dependencies] 15 | err-derive = "" 16 | #failure = "" 17 | #failure_derive = "" 18 | futures-preview = "= 0.3.0-alpha.17" 19 | libc = "" 20 | 21 | [build-dependencies] 22 | bindgen = "= 0.41.0" 23 | 24 | [dev-dependencies] 25 | hamcrest2 = "*" 26 | starfish-executor = { path = "../starfish-executor" } 27 | -------------------------------------------------------------------------------- /spdk-sys/build.rs: -------------------------------------------------------------------------------- 1 | extern crate bindgen; 2 | 3 | use std::env; 4 | use std::path::Path; 5 | use std::path::PathBuf; 6 | 7 | static SPDK_INCLUDE_DIR: &'static str = "/usr/local/include"; 8 | 9 | fn generate_bindings() { 10 | let spdk_include_path = env::var("SPDK_INCLUDE").unwrap_or(SPDK_INCLUDE_DIR.to_string()); 11 | let output_path = env::var("OUT_DIR").unwrap(); 12 | let generator = Generator { 13 | spdk_include_path: Path::new(&spdk_include_path), 14 | output_path: Path::new(&output_path), 15 | }; 16 | 17 | let headers = [ 18 | "nvme", 19 | "event", 20 | "bdev", 21 | "env", 22 | "blob_bdev", 23 | "blob", 24 | "log", 25 | "io_channel", 26 | ]; 27 | generator.generate(&headers) 28 | } 29 | 30 | struct Generator<'a> { 31 | spdk_include_path: &'a Path, 32 | output_path: &'a Path, 33 | } 34 | 35 | impl<'a> Generator<'a> { 36 | fn generate(&self, names: &[&str]) { 37 | let mut codegen_config = bindgen::CodegenConfig::empty(); 38 | codegen_config.set(bindgen::CodegenConfig::FUNCTIONS, true); 39 | codegen_config.set(bindgen::CodegenConfig::TYPES, true); 40 | 41 | let mut builder = bindgen::builder(); 42 | 43 | for name in names { 44 | let header_path = self.spdk_include_path.join( 45 | PathBuf::from("spdk/header.h") 46 | .with_file_name(name) 47 | .with_extension("h"), 48 | ); 49 | builder = builder.header(format!("{}", header_path.display())); 50 | } 51 | 52 | let bindings = builder 53 | .derive_default(true) 54 | .with_codegen_config(codegen_config) 55 | .generate_inline_functions(false) 56 | // If there are linking errors and the generated bindings have weird looking 57 | // #link_names (that start with \u{1}), the make sure to flip that to false. 58 | .trust_clang_mangling(false) 59 | .rustfmt_bindings(true) 60 | .rustfmt_configuration_file(Some(PathBuf::from("../rustfmt.toml"))) 61 | .layout_tests(false) 62 | .ctypes_prefix("libc") 63 | .generate() 64 | .expect("Unable to generate bindings"); 65 | 66 | bindings 67 | .write_to_file(self.output_path.join("spdk_bindings.rs")) 68 | .expect("Couldn't write bindings!"); 69 | } 70 | } 71 | 72 | fn main() { 73 | generate_bindings(); 74 | println!("cargo:rerun-if-changed=build.rs"); 75 | println!("cargo:rustc-link-lib=spdk"); 76 | println!("cargo:rustc-link-search=native=/usr/local/lib"); 77 | } 78 | -------------------------------------------------------------------------------- /spdk-sys/config/hello_blob.conf: -------------------------------------------------------------------------------- 1 | [Global] 2 | Comment "Global section" 3 | ReactorMask 0x1 4 | 5 | [Malloc] 6 | NumberOfLuns 1 7 | LunSizeInMB 16 8 | 9 | [AIO] 10 | AIO /tmp/aiofile AIO1 2048 -------------------------------------------------------------------------------- /spdk-sys/src/bdev.rs: -------------------------------------------------------------------------------- 1 | use crate::generated::{spdk_bdev, spdk_bdev_get_by_name}; 2 | use std::ffi::CString; 3 | 4 | #[derive(Error, Debug)] 5 | pub enum BDevError { 6 | #[error(display = "Could not find a bdev: {}", _0)] 7 | NotFound(String), 8 | } 9 | 10 | /// SPDK block device. 11 | /// TODO: Implement Drop 12 | #[derive(Debug)] 13 | pub struct BDev { 14 | pub(crate) name: String, 15 | pub(crate) bdev: *mut spdk_bdev, 16 | } 17 | 18 | pub fn get_by_name(name: S) -> Result 19 | where 20 | S: Into + Clone, 21 | { 22 | let name_cstring = CString::new(name.clone().into()).expect("Couldn't create a string"); 23 | 24 | let bdev = unsafe { spdk_bdev_get_by_name(name_cstring.as_ptr()) }; 25 | if bdev.is_null() { 26 | return Err(BDevError::NotFound(name.clone().into())); 27 | } 28 | 29 | Ok(BDev { 30 | name: name.into(), 31 | bdev, 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /spdk-sys/src/blob.rs: -------------------------------------------------------------------------------- 1 | use crate::blob_bdev::BlobStoreBDev; 2 | use crate::env::Buf; 3 | use crate::generated::{ 4 | spdk_blob, spdk_blob_close, spdk_blob_get_id, spdk_blob_get_num_clusters, spdk_blob_id, 5 | spdk_blob_io_read, spdk_blob_io_write, spdk_blob_resize, spdk_blob_store, spdk_blob_sync_md, 6 | spdk_bs_alloc_io_channel, spdk_bs_create_blob, spdk_bs_delete_blob, spdk_bs_free_cluster_count, 7 | spdk_bs_free_io_channel, spdk_bs_get_page_size, spdk_bs_init, spdk_bs_open_blob, 8 | spdk_bs_unload, spdk_io_channel, 9 | }; 10 | use futures::channel::oneshot; 11 | use futures::channel::oneshot::Sender; 12 | use libc::c_int; 13 | use libc::c_void; 14 | use std::fmt; 15 | use std::fmt::Debug; 16 | use std::ptr; 17 | 18 | #[derive(Debug, Error)] 19 | pub enum BlobstoreError { 20 | #[error(display = "Failed to initialize blob store: {}", _0)] 21 | InitError(i32), 22 | 23 | #[error(display = "Failed to allocate io channel")] 24 | IoChannelAllocateError, 25 | 26 | #[error(display = "Failed to unload blob store: {}", _0)] 27 | UnloadError(i32), 28 | } 29 | 30 | #[derive(Debug)] 31 | pub struct Blobstore { 32 | pub(crate) blob_store: *mut spdk_blob_store, 33 | } 34 | 35 | impl Blobstore { 36 | pub fn get_page_size(&self) -> u64 { 37 | unsafe { spdk_bs_get_page_size(self.blob_store) } 38 | } 39 | 40 | pub fn get_free_cluster_count(&self) -> u64 { 41 | unsafe { spdk_bs_free_cluster_count(self.blob_store) } 42 | } 43 | 44 | pub fn alloc_io_channel(&mut self) -> Result { 45 | let io_channel = unsafe { spdk_bs_alloc_io_channel(self.blob_store) }; 46 | if io_channel.is_null() { 47 | return Err(BlobstoreError::IoChannelAllocateError); 48 | } 49 | Ok(IoChannel { io_channel }) 50 | } 51 | } 52 | 53 | #[derive(Debug, Error)] 54 | pub enum BlobError { 55 | #[error(display = "Failed to create blob: {}", _0)] 56 | CreateError(i32), 57 | 58 | #[error(display = "Failed to open blob({}): {}", _0, _1)] 59 | OpenError(BlobId, i32), 60 | 61 | #[error(display = "Failed to resize blob({}): {}", _0, _1)] 62 | ResizeError(BlobId, i32), 63 | 64 | #[error(display = "Failed to sync metadata for blob({}): {}", _0, _1)] 65 | SyncError(BlobId, i32), 66 | 67 | #[error( 68 | display = "Error in write completion({}): {}, offset: {}, length: {}", 69 | _0, 70 | _1, 71 | _2, 72 | _3 73 | )] 74 | WriteError(BlobId, i32, u64, u64), 75 | 76 | #[error( 77 | display = "Error in read completion({}): {}, offset: {}, length: {}", 78 | _0, 79 | _1, 80 | _2, 81 | _3 82 | )] 83 | ReadError(BlobId, i32, u64, u64), 84 | 85 | #[error(display = "Failed to close blob: {}", _0)] 86 | CloseError(i32), 87 | 88 | #[error(display = "Failed to delete blob({}): {}", _0, _1)] 89 | DeleteError(BlobId, i32), 90 | } 91 | 92 | #[derive(Debug, Clone, Copy)] 93 | pub struct BlobId { 94 | pub(crate) blob_id: spdk_blob_id, 95 | } 96 | 97 | impl fmt::Display for BlobId { 98 | fn fmt<'a>(&self, f: &mut fmt::Formatter<'a>) -> fmt::Result { 99 | write!(f, "{:?}", self) 100 | } 101 | } 102 | 103 | #[derive(Debug)] 104 | pub struct Blob { 105 | pub(crate) blob: *mut spdk_blob, 106 | } 107 | 108 | impl Blob { 109 | pub fn get_num_clusters(&self) -> u64 { 110 | unsafe { spdk_blob_get_num_clusters(self.blob) } 111 | } 112 | 113 | pub fn get_blob_id(&self) -> BlobId { 114 | let blob_id = unsafe { spdk_blob_get_id(self.blob) }; 115 | BlobId { blob_id } 116 | } 117 | } 118 | 119 | // TODO: Drop for Blob 120 | 121 | pub struct IoChannel { 122 | pub(crate) io_channel: *mut spdk_io_channel, 123 | } 124 | 125 | impl Drop for IoChannel { 126 | fn drop(&mut self) { 127 | unsafe { spdk_bs_free_io_channel(self.io_channel) }; 128 | } 129 | } 130 | 131 | // TODO: Implement Drop correctly with a call to spdk_bs_unload: 132 | // Funny thing is that this is async, so will be interesting to see how to do that? 133 | // I can't block 134 | 135 | /// Initialize a blobstore on the given device. 136 | pub async fn bs_init(bs_dev: &mut BlobStoreBDev) -> Result { 137 | let (sender, receiver) = oneshot::channel(); 138 | 139 | unsafe { 140 | spdk_bs_init( 141 | bs_dev.bs_dev, 142 | ptr::null_mut(), 143 | Some(complete_callback_1::<*mut spdk_blob_store>), 144 | cb_arg(sender), 145 | ); 146 | } 147 | 148 | let res = receiver.await.expect("Cancellation is not supported"); 149 | 150 | match res { 151 | Ok(blob_store) => Ok(Blobstore { blob_store }), 152 | Err(bserrno) => Err(BlobstoreError::InitError(bserrno)), 153 | } 154 | } 155 | 156 | pub async fn bs_unload(blob_store: Blobstore) -> Result<(), BlobstoreError> { 157 | let (sender, receiver) = oneshot::channel(); 158 | unsafe { 159 | spdk_bs_unload( 160 | blob_store.blob_store, 161 | Some(complete_callback_0), 162 | cb_arg::<()>(sender), 163 | ); 164 | } 165 | let res = receiver.await.expect("Cancellation is not supported"); 166 | 167 | match res { 168 | Ok(()) => Ok(()), 169 | Err(bserrno) => Err(BlobstoreError::UnloadError(bserrno)), 170 | } 171 | } 172 | 173 | pub async fn create(blob_store: &Blobstore) -> Result { 174 | let (sender, receiver) = oneshot::channel(); 175 | unsafe { 176 | spdk_bs_create_blob( 177 | blob_store.blob_store, 178 | Some(complete_callback_1::), 179 | cb_arg(sender), 180 | ); 181 | } 182 | let res = receiver.await.expect("Cancellation is not supported"); 183 | 184 | match res { 185 | Ok(blob_id) => Ok(BlobId { blob_id }), 186 | Err(bserrno) => Err(BlobError::CreateError(bserrno)), 187 | } 188 | } 189 | 190 | pub async fn open(blob_store: &Blobstore, blob_id: BlobId) -> Result { 191 | let (sender, receiver) = oneshot::channel(); 192 | unsafe { 193 | spdk_bs_open_blob( 194 | blob_store.blob_store, 195 | blob_id.blob_id, 196 | Some(complete_callback_1::<*mut spdk_blob>), 197 | cb_arg(sender), 198 | ); 199 | } 200 | let res = receiver.await.expect("Cancellation is not supported"); 201 | 202 | match res { 203 | Ok(blob) => Ok(Blob { blob }), 204 | Err(bserrno) => Err(BlobError::OpenError(blob_id, bserrno)), 205 | } 206 | } 207 | 208 | pub async fn resize(blob: &Blob, required_size: u64) -> Result<(), BlobError> { 209 | let (sender, receiver) = oneshot::channel(); 210 | unsafe { 211 | spdk_blob_resize( 212 | blob.blob, 213 | required_size, 214 | Some(complete_callback_0), 215 | cb_arg::<()>(sender), 216 | ); 217 | } 218 | let res = receiver.await.expect("Cancellation is not supported"); 219 | 220 | match res { 221 | Ok(()) => Ok(()), 222 | Err(bserrno) => Err(BlobError::ResizeError(blob.get_blob_id(), bserrno)), 223 | } 224 | } 225 | 226 | /** 227 | * Sync a blob. 228 | * 229 | * Make a blob persistent. This applies to open, resize, set xattr, and remove 230 | * xattr. These operations will not be persistent until the blob has been synced. 231 | * 232 | * \param blob Blob to sync. 233 | * \param cb_fn Called when the operation is complete. 234 | * \param cb_arg Argument passed to function cb_fn. 235 | */ 236 | /// Metadata is stored in volatile memory for performance 237 | /// reasons and therefore needs to be synchronized with 238 | /// non-volatile storage to make it persistent. This can be 239 | /// done manually, as shown here, or if not it will be done 240 | /// automatically when the blob is closed. It is always a 241 | /// good idea to sync after making metadata changes unless 242 | /// it has an unacceptable impact on application performance. 243 | pub async fn sync_metadata(blob: &Blob) -> Result<(), BlobError> { 244 | let (sender, receiver) = oneshot::channel(); 245 | unsafe { 246 | spdk_blob_sync_md(blob.blob, Some(complete_callback_0), cb_arg::<()>(sender)); 247 | } 248 | let res = receiver.await.expect("Cancellation is not supported"); 249 | 250 | match res { 251 | Ok(()) => Ok(()), 252 | Err(bserrno) => Err(BlobError::SyncError(blob.get_blob_id(), bserrno)), 253 | } 254 | } 255 | 256 | /// Write data to a blob. 257 | /// 258 | /// \param blob Blob to write. 259 | /// \param channel The I/O channel used to submit requests. 260 | /// \param payload The specified buffer which should contain the data to be written. 261 | /// \param offset Offset is in pages from the beginning of the blob. 262 | /// \param length Size of data in pages. 263 | /// \param cb_fn Called when the operation is complete. 264 | /// \param cb_arg Argument passed to function cb_fn. 265 | /// TODO: the interface here is funky as is, needs work; 266 | /// Specifically writes need to happen in pages, so the buf abstraction should probably enforce that. 267 | /// Similarly, spdk_blob_io_writev is probably the more interesting case if we don't want to 268 | /// have to do copies. 269 | pub async fn write<'a>( 270 | blob: &'a Blob, 271 | io_channel: &'a IoChannel, 272 | buf: &'a Buf, 273 | offset: u64, 274 | length: u64, 275 | ) -> Result<(), BlobError> { 276 | let (sender, receiver) = oneshot::channel(); 277 | unsafe { 278 | spdk_blob_io_write( 279 | blob.blob, 280 | io_channel.io_channel, 281 | buf.ptr, 282 | offset, 283 | length, 284 | Some(complete_callback_0), 285 | cb_arg::<()>(sender), 286 | ); 287 | } 288 | let res = receiver.await.expect("Cancellation is not supported"); 289 | 290 | match res { 291 | Ok(()) => Ok(()), 292 | Err(bserrno) => Err(BlobError::WriteError( 293 | blob.get_blob_id(), 294 | bserrno, 295 | offset, 296 | length, 297 | )), 298 | } 299 | } 300 | 301 | pub async fn read<'a>( 302 | blob: &'a Blob, 303 | io_channel: &'a IoChannel, 304 | buf: &'a Buf, 305 | offset: u64, 306 | length: u64, 307 | ) -> Result<(), BlobError> { 308 | let (sender, receiver) = oneshot::channel(); 309 | unsafe { 310 | spdk_blob_io_read( 311 | blob.blob, 312 | io_channel.io_channel, 313 | buf.ptr, 314 | offset, 315 | length, 316 | Some(complete_callback_0), 317 | cb_arg::<()>(sender), 318 | ); 319 | } 320 | let res = receiver.await.expect("Cancellation is not supported"); 321 | 322 | match res { 323 | Ok(()) => Ok(()), 324 | Err(bserrno) => Err(BlobError::ReadError( 325 | blob.get_blob_id(), 326 | bserrno, 327 | offset, 328 | length, 329 | )), 330 | } 331 | } 332 | 333 | pub async fn close(blob: Blob) -> Result<(), BlobError> { 334 | let (sender, receiver) = oneshot::channel(); 335 | unsafe { 336 | spdk_blob_close(blob.blob, Some(complete_callback_0), cb_arg::<()>(sender)); 337 | } 338 | let res = receiver.await.expect("Cancellation is not supported"); 339 | 340 | match res { 341 | Ok(()) => Ok(()), 342 | Err(bserrno) => Err(BlobError::CloseError(bserrno)), 343 | } 344 | } 345 | 346 | pub async fn delete(blob_store: &Blobstore, blob_id: BlobId) -> Result<(), BlobError> { 347 | let (sender, receiver) = oneshot::channel(); 348 | unsafe { 349 | spdk_bs_delete_blob( 350 | blob_store.blob_store, 351 | blob_id.blob_id, 352 | Some(complete_callback_0), 353 | cb_arg::<()>(sender), 354 | ); 355 | } 356 | let res = receiver.await.expect("Cancellation is not supported"); 357 | 358 | match res { 359 | Ok(()) => Ok(()), 360 | Err(bserrno) => Err(BlobError::DeleteError(blob_id, bserrno)), 361 | } 362 | } 363 | 364 | fn cb_arg(sender: Sender>) -> *mut c_void { 365 | Box::into_raw(Box::new(sender)) as *const _ as *mut c_void 366 | } 367 | 368 | extern "C" fn complete_callback_0(sender_ptr: *mut c_void, bserrno: c_int) { 369 | let sender = unsafe { Box::from_raw(sender_ptr as *mut Sender>) }; 370 | let ret = if bserrno != 0 { Err(bserrno) } else { Ok(()) }; 371 | sender.send(ret).expect("Receiver is gone"); 372 | } 373 | 374 | extern "C" fn complete_callback_1(sender_ptr: *mut c_void, bs: T, bserrno: c_int) 375 | where 376 | T: Debug, 377 | { 378 | let sender = unsafe { Box::from_raw(sender_ptr as *mut Sender>) }; 379 | let ret = if bserrno != 0 { Err(bserrno) } else { Ok(bs) }; 380 | sender.send(ret).expect("Receiver is gone"); 381 | } 382 | -------------------------------------------------------------------------------- /spdk-sys/src/blob_bdev.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | use crate::bdev::BDev; 4 | use crate::generated::{spdk_bdev_create_bs_dev, spdk_bs_dev}; 5 | 6 | #[derive(Debug, Error)] 7 | pub enum BlobBDevError { 8 | #[error(display = "Could not create blob bdev!: {}", _0)] 9 | FailedToCreate(String), 10 | } 11 | 12 | /// SPDK blob store block device. 13 | /// 14 | /// This is a virtual representation of a block device that is exported by the backend. 15 | /// TODO: Implement Drop 16 | #[derive(Debug)] 17 | pub struct BlobStoreBDev { 18 | pub(crate) bs_dev: *mut spdk_bs_dev, 19 | } 20 | 21 | pub fn create_bs_dev(bdev: &mut BDev) -> Result { 22 | let bs_dev = unsafe { spdk_bdev_create_bs_dev(bdev.bdev, None, ptr::null_mut()) }; 23 | 24 | if bs_dev.is_null() { 25 | return Err(BlobBDevError::FailedToCreate(bdev.name.clone())); 26 | } 27 | 28 | Ok(BlobStoreBDev { bs_dev }) 29 | } 30 | -------------------------------------------------------------------------------- /spdk-sys/src/env.rs: -------------------------------------------------------------------------------- 1 | use crate::generated::{spdk_dma_free, spdk_dma_malloc}; 2 | use libc::c_void; 3 | use std::ptr; 4 | 5 | pub struct Buf { 6 | pub(crate) len: usize, 7 | pub(crate) ptr: *mut c_void, 8 | } 9 | 10 | impl PartialEq for Buf { 11 | fn eq(&self, other: &Buf) -> bool { 12 | if self.len != other.len { 13 | return false; 14 | } 15 | 16 | let ret = unsafe { libc::memcmp(self.ptr, other.ptr, self.len) }; 17 | 18 | ret == 0 19 | } 20 | } 21 | 22 | impl Buf { 23 | pub fn fill(&mut self, b: i8) { 24 | unsafe { 25 | libc::memset(self.ptr, self.len as i32, b as usize); 26 | } 27 | } 28 | } 29 | 30 | impl Drop for Buf { 31 | fn drop(&mut self) { 32 | unsafe { spdk_dma_free(self.ptr) } 33 | } 34 | } 35 | 36 | pub fn dma_malloc(size: u64, align: usize) -> Buf { 37 | let ptr = unsafe { spdk_dma_malloc(size as usize, align, ptr::null_mut() as *mut u64) }; 38 | assert!(!ptr.is_null(), "Failed to malloc"); 39 | Buf { 40 | len: size as usize, 41 | ptr, 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /spdk-sys/src/event.rs: -------------------------------------------------------------------------------- 1 | use crate::generated::{ 2 | spdk_app_fini, spdk_app_opts, spdk_app_opts_init, spdk_app_start, spdk_app_stop, 3 | }; 4 | use libc::c_char; 5 | use libc::c_void; 6 | use std::ffi::CString; 7 | use std::ptr; 8 | 9 | #[derive(Debug, Error)] 10 | pub enum AppError { 11 | #[error(display = "Spdk failed to start: {}", _0)] 12 | StartupError(i32), 13 | } 14 | 15 | #[derive(Default)] 16 | pub struct AppOpts(spdk_app_opts); 17 | 18 | impl AppOpts { 19 | pub fn new() -> Self { 20 | let mut opts: spdk_app_opts = Default::default(); 21 | unsafe { 22 | spdk_app_opts_init(&mut opts as *mut spdk_app_opts); 23 | } 24 | AppOpts(opts) 25 | } 26 | 27 | pub fn name(&mut self, name: &str) { 28 | self.0.name = CString::new(name) 29 | .expect("Couldn't create a string") 30 | .into_raw() 31 | } 32 | 33 | pub fn config_file(&mut self, config_file: &str) { 34 | self.0.config_file = CString::new(config_file) 35 | .expect("Couldn't create a string") 36 | .into_raw() 37 | } 38 | 39 | // TODO: probably need this to properly deallocate pollers :() 40 | // pub fn shutdown_cb() { 41 | // //spdk_app_shutdown_cb 42 | // } 43 | 44 | pub fn start(mut self, f: F) -> Result<(), AppError> 45 | where 46 | F: Fn() -> (), 47 | { 48 | let user_data = &f as *const _ as *mut c_void; 49 | 50 | extern "C" fn start_wrapper(closure: *mut c_void, _: *mut c_void) 51 | where 52 | F: Fn() -> (), 53 | { 54 | let opt_closure = closure as *mut F; 55 | unsafe { (*opt_closure)() } 56 | } 57 | 58 | let ret = unsafe { 59 | let self_ref = &mut self; 60 | let opts_ref = &mut self_ref.0; 61 | spdk_app_start( 62 | opts_ref as *mut spdk_app_opts, 63 | Some(start_wrapper::), 64 | user_data, 65 | ptr::null_mut(), 66 | ) 67 | }; 68 | 69 | unsafe { 70 | spdk_app_fini(); 71 | } 72 | 73 | if ret == 0 { 74 | Ok(()) 75 | } else { 76 | Err(AppError::StartupError(ret)) 77 | } 78 | } 79 | } 80 | 81 | pub fn app_stop(success: bool) { 82 | unsafe { 83 | spdk_app_stop(if success { 0 } else { -1 }); 84 | }; 85 | } 86 | 87 | impl Drop for AppOpts { 88 | fn drop(&mut self) { 89 | drop_if_not_null(self.0.name as *mut c_char); 90 | drop_if_not_null(self.0.config_file as *mut c_char); 91 | } 92 | } 93 | 94 | fn drop_if_not_null(string: *mut c_char) { 95 | if !string.is_null() { 96 | unsafe { CString::from_raw(string as *mut c_char) }; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /spdk-sys/src/generated/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(warnings)] 2 | #![allow(clippy)] 3 | #![allow(unknown_lints)] 4 | include!(concat!(env!("OUT_DIR"), "/spdk_bindings.rs")); 5 | -------------------------------------------------------------------------------- /spdk-sys/src/io_channel.rs: -------------------------------------------------------------------------------- 1 | use libc::c_int; 2 | use libc::c_void; 3 | 4 | use crate::generated::{spdk_poller, spdk_poller_register /*spdk_poller_unregister*/}; 5 | 6 | pub struct PollerHandle { 7 | #[allow(dead_code)] 8 | pub(crate) poller: *mut spdk_poller, 9 | #[allow(dead_code)] 10 | pub(crate) closure: Box bool>, 11 | } 12 | 13 | impl Drop for PollerHandle { 14 | #[allow(clippy::cast_ptr_alignment)] 15 | fn drop(&mut self) { 16 | // TODO(jkozlowski): Fix this up eventually, it somehow causes a double-free. 17 | //let tmp_poller = self.poller; 18 | // This is rather dogdy, spdk_poller_unregister will write NULL to self.poller, 19 | // hopefully that isn't going to crash! 20 | //unsafe { spdk_poller_unregister(tmp_poller as *mut *mut spdk_poller) } 21 | } 22 | } 23 | 24 | /// Registers a poller with spdk. 25 | /// f: should return true if any work was done 26 | pub fn poller_register(f: F) -> PollerHandle 27 | where 28 | F: Fn() -> bool + 'static, 29 | { 30 | extern "C" fn poller_wrapper(closure: *mut c_void) -> c_int 31 | where 32 | F: Fn() -> bool, 33 | { 34 | let opt_closure = closure as *mut F; 35 | let work_done = unsafe { (*opt_closure)() }; 36 | if work_done { 37 | 1 38 | } else { 39 | 0 40 | } 41 | } 42 | 43 | let f_raw = Box::into_raw(Box::new(f)) as *mut dyn Fn() -> bool; 44 | let f_pointer = f_raw as *const _ as *mut c_void; 45 | let poller = unsafe { spdk_poller_register(Some(poller_wrapper::), f_pointer, 0) }; 46 | PollerHandle { 47 | // TODO: handle failure 48 | poller, 49 | closure: unsafe { Box::from_raw(f_raw) }, 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /spdk-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(rust_2018_idioms)] 2 | #![feature(async_await)] 3 | #![feature(nll)] 4 | #![allow(macro_use_extern_crate)] 5 | #[macro_use] 6 | extern crate err_derive; 7 | 8 | mod generated; 9 | 10 | pub mod bdev; 11 | 12 | pub mod blob; 13 | pub mod blob_bdev; 14 | 15 | pub mod env; 16 | pub mod event; 17 | pub mod io_channel; 18 | 19 | #[cfg(test)] 20 | #[macro_use] 21 | extern crate hamcrest2; 22 | 23 | #[cfg(test)] 24 | mod ete_test { 25 | 26 | use crate::bdev; 27 | use crate::blob; 28 | use crate::blob_bdev; 29 | use crate::env as spdk_env; 30 | use crate::event; 31 | use crate::io_channel; 32 | use crate::io_channel::PollerHandle; 33 | use hamcrest2::prelude::*; 34 | use starfish_executor as executor; 35 | use std::error::Error; 36 | use std::mem; 37 | use std::path::Path; 38 | 39 | #[test] 40 | pub fn ete_test() { 41 | let config_file = Path::new("config/hello_blob.conf").canonicalize().unwrap(); 42 | let mut opts = event::AppOpts::new(); 43 | 44 | opts.name("hello_blob"); 45 | opts.config_file(config_file.to_str().unwrap()); 46 | 47 | let ret = opts.start(|| { 48 | let executor = executor::initialize(); 49 | 50 | // TODO: fixup 51 | mem::forget(executor); 52 | 53 | // Register the executor poller 54 | let poller = io_channel::poller_register(executor::pure_poll); 55 | 56 | executor::spawn(run(poller)); 57 | }); 58 | 59 | assert_that!(ret, is(ok())); 60 | } 61 | 62 | async fn run(poller: PollerHandle) { 63 | match run_inner().await { 64 | Ok(_) => println!("Successful"), 65 | Err(err) => println!("Failure: {:?}", err), 66 | } 67 | 68 | drop(poller); 69 | 70 | event::app_stop(true); 71 | } 72 | 73 | async fn run_inner() -> Result<(), Box> { 74 | let mut bdev = bdev::get_by_name("AIO1")?; 75 | println!("{:?}", bdev); 76 | 77 | let mut bs_dev = blob_bdev::create_bs_dev(&mut bdev)?; 78 | println!("{:?}", bs_dev); 79 | 80 | let mut blobstore = blob::bs_init(&mut bs_dev).await?; 81 | 82 | run_with_blob_store(&mut blobstore).await?; 83 | 84 | blob::bs_unload(blobstore).await?; 85 | 86 | Ok(()) 87 | } 88 | 89 | async fn run_with_blob_store(blobstore: &mut blob::Blobstore) -> Result<(), Box> { 90 | let page_size = blobstore.get_page_size(); 91 | 92 | println!("Page size: {:?}", page_size); 93 | 94 | let blob_id = blob::create(&blobstore).await?; 95 | 96 | println!("Blob created: {:?}", blob_id); 97 | 98 | let blob = blob::open(&blobstore, blob_id).await?; 99 | 100 | println!("Opened blob"); 101 | 102 | let free_clusters = blobstore.get_free_cluster_count(); 103 | println!("blobstore has FREE clusters of {:?}", free_clusters); 104 | 105 | blob::resize(&blob, free_clusters).await?; 106 | 107 | let total = blob.get_num_clusters(); 108 | println!("resized blob now has USED clusters of {}", total); 109 | 110 | blob::sync_metadata(&blob).await?; 111 | 112 | println!("metadata sync complete"); 113 | 114 | /* 115 | * Buffers for data transfer need to be allocated via SPDK. We will 116 | * tranfer 1 page of 4K aligned data at offset 0 in the blob. 117 | */ 118 | let mut write_buf = spdk_env::dma_malloc(page_size, 0x1000); 119 | write_buf.fill(0x5a); 120 | 121 | /* Now we have to allocate a channel. */ 122 | let channel = blobstore.alloc_io_channel()?; 123 | 124 | /* Let's perform the write, 1 page at offset 0. */ 125 | println!("Starting write"); 126 | blob::write(&blob, &channel, &write_buf, 0, 1).await?; 127 | println!("Finished writing"); 128 | 129 | let read_buf = spdk_env::dma_malloc(page_size, 0x1000); 130 | 131 | /* Issue the read */ 132 | println!("Starting read"); 133 | blob::read(&blob, &channel, &read_buf, 0, 1).await?; 134 | println!("Finished read"); 135 | 136 | /* Now let's make sure things match. */ 137 | if write_buf != read_buf { 138 | println!("Error in data compare"); 139 | // unload_bs(hello_context, "Error in data compare", -1); 140 | // return; 141 | } else { 142 | println!("read SUCCESS and data matches!"); 143 | } 144 | 145 | /* Now let's close it and delete the blob in the callback. */ 146 | blob::close(blob).await?; 147 | println!("Closed"); 148 | 149 | blob::delete(&blobstore, blob_id).await?; 150 | 151 | println!("Deleted"); 152 | 153 | Ok(()) 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // To make cargo happy. 2 | -------------------------------------------------------------------------------- /starfish-example-app/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "starfish-example-app" 3 | version = "0.1.0" 4 | authors = ["Jakub Kozlowski "] 5 | description = """ 6 | Example app that uses the APIs. 7 | """ 8 | edition = '2018' 9 | 10 | [dependencies] 11 | spdk-sys = { path = "../spdk-sys" } 12 | starfish-executor = { path = "../starfish-executor" } 13 | 14 | failure = "" 15 | futures-preview = "= 0.3.0-alpha.17" -------------------------------------------------------------------------------- /starfish-example-app/config/hello_blob.conf: -------------------------------------------------------------------------------- 1 | [Global] 2 | Comment "Global section" 3 | ReactorMask 0x1 4 | 5 | [Malloc] 6 | NumberOfLuns 1 7 | LunSizeInMB 16 8 | 9 | [AIO] 10 | AIO /dev/ram0 AIO0 11 | AIO /tmp/aiofile AIO1 2048 -------------------------------------------------------------------------------- /starfish-example-app/src/main.rs: -------------------------------------------------------------------------------- 1 | #![warn(rust_2018_idioms)] 2 | #![feature(async_await, nll)] 3 | 4 | pub fn main() {} 5 | -------------------------------------------------------------------------------- /starfish-executor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "starfish-executor" 3 | version = "0.1.0" 4 | authors = ["Jakub Kozlowski "] 5 | description = """ 6 | Single threaded executor for futures-rs. No Arc in sight. 7 | """ 8 | 9 | [dependencies] 10 | futures-preview = "= 0.3.0-alpha.17" 11 | 12 | [dev-dependencies] 13 | hamcrest2 = "*" 14 | -------------------------------------------------------------------------------- /starfish-executor/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(rust_2018_idioms)] 2 | #![feature(arbitrary_self_types, nll)] 3 | 4 | extern crate futures; 5 | 6 | macro_rules! waker_vtable { 7 | ($ty:ident) => { 8 | &RawWakerVTable::new( 9 | clone_rc_raw::<$ty>, 10 | wake_rc_raw::<$ty>, 11 | wake_by_ref_rc_raw::<$ty>, 12 | drop_rc_raw::<$ty>, 13 | ) 14 | }; 15 | } 16 | 17 | pub mod waker; 18 | pub mod waker_ref; 19 | 20 | use crate::waker::RcWake; 21 | use futures::future::LocalFutureObj; 22 | use futures::task::LocalSpawn; 23 | use futures::task::SpawnError; 24 | use std::cell::Cell; 25 | use std::cell::RefCell; 26 | use std::cell::UnsafeCell; 27 | use std::collections::VecDeque; 28 | use std::future::Future; 29 | use std::pin::Pin; 30 | use std::rc::Rc; 31 | use std::task::Context; 32 | use std::task::Poll; 33 | 34 | // Tracks the executor for the current execution context. 35 | // TODO: Use UnsafeCell, since we can guarantee correct implementation 36 | thread_local!(static CURRENT_EXECUTOR: RefCell> = RefCell::new(None)); 37 | 38 | #[allow(dead_code)] 39 | struct LocalExecutor {} 40 | 41 | impl LocalExecutor { 42 | #[allow(dead_code)] 43 | fn new() -> Self { 44 | LocalExecutor {} 45 | } 46 | } 47 | 48 | impl LocalSpawn for LocalExecutor { 49 | fn spawn_local_obj(&mut self, future: LocalFutureObj<'static, ()>) -> Result<(), SpawnError> { 50 | with_default_no_fail(|maybe_executor| match maybe_executor { 51 | Some(ref executor) => { 52 | let task = Rc::new(TaskHandle { 53 | task: UnsafeCell::new(Some(TaskContext::new(future))), 54 | queued: Cell::new(true), 55 | }); 56 | (&mut executor.tq.borrow_mut()).add_task(task); 57 | Ok(()) 58 | } 59 | None => Err(SpawnError::shutdown()), 60 | }) 61 | } 62 | } 63 | 64 | pub struct CurrentThreadExecutor { 65 | // Mutable interior is required, in case a future 66 | // asks to be woken up while we are in `pure_poll`. 67 | // TODO: use UnsafeCell, since we know the usage is correct and apparently there is some overhead. 68 | tq: RefCell, 69 | } 70 | 71 | impl CurrentThreadExecutor { 72 | fn new() -> CurrentThreadExecutor { 73 | CurrentThreadExecutor { 74 | tq: RefCell::new(TaskQueue::new()), 75 | } 76 | } 77 | } 78 | 79 | struct TaskQueue { 80 | q: VecDeque>, 81 | } 82 | 83 | impl TaskQueue { 84 | #[inline] 85 | fn new() -> TaskQueue { 86 | TaskQueue { q: VecDeque::new() } 87 | } 88 | 89 | #[inline] 90 | fn add_task(&mut self, task: Rc) { 91 | self.q.push_back(task) 92 | } 93 | 94 | /// Polls the next `TaskHandle` and gets it as a raw pointer from `Rc`. 95 | /// The counter is not incremented, the pointer owns one reference. 96 | #[inline] 97 | fn poll_task_from_rc(&mut self) -> Option<*const TaskHandle> { 98 | self.q.pop_front().map(Rc::into_raw) 99 | } 100 | } 101 | 102 | struct TaskHandle { 103 | task: UnsafeCell>, 104 | queued: Cell, 105 | } 106 | 107 | impl Drop for TaskHandle { 108 | fn drop(&mut self) { 109 | unsafe { 110 | if (*self.task.get()).is_some() { 111 | // TODO(jkozlowski) Fix this up. 112 | //abort("future still here when dropping"); 113 | } 114 | } 115 | } 116 | } 117 | 118 | // TODO: Revisit this, even though we only ever want to run local futures, 119 | // however I'll need to figure out how to have local counters that 120 | // send a message once the value reaches zero 121 | // to the original core. 122 | // 123 | // It is likely that CurrentThreadExecutor will need to store it's ID and 124 | // a function that will let it notify 125 | // other cores (that just maps to dpdk queues); Then TaskHandle also needs 126 | // to know which core it is on 127 | // and then UnsafeWake impls will actually do nested Rcs -> there is basically 128 | // a counter on each core 129 | // that has access and if a particular core's handle goes to 0, it notifies 130 | // the core that sent it the handle, 131 | // so they form a cycle? 132 | // 133 | // Not sure if this is even possible, so for now let's pretend 134 | // we'll never send handles to other cores. 135 | unsafe impl Send for TaskHandle {} 136 | unsafe impl Sync for TaskHandle {} 137 | 138 | impl RcWake for TaskHandle { 139 | fn wake_by_ref(rc_self: &Rc) { 140 | if !rc_self.queued.replace(true) { 141 | CURRENT_EXECUTOR.with(|current| { 142 | if let Some(ref current_thread) = *current.borrow() { 143 | // Note that we don't change the reference count of the task here, 144 | // we merely enqueue the raw pointer. The `pure_poll` 145 | // implementation guarantees that if we set the `queued` flag that 146 | // there's a reference count held by the main `pure_poll` queue 147 | // still. 148 | // TODO(jkozlowski) Fix this clone 149 | current_thread.tq.borrow_mut().add_task(rc_self.clone()); 150 | } 151 | }); 152 | } 153 | } 154 | } 155 | 156 | struct TaskContext { 157 | fut: LocalFutureObj<'static, ()>, 158 | } 159 | 160 | impl TaskContext { 161 | fn new(future: F) -> TaskContext 162 | where 163 | F: Future + 'static, 164 | { 165 | TaskContext { 166 | fut: Box::new(future).into(), 167 | } 168 | } 169 | } 170 | 171 | pub struct Enter {} 172 | 173 | impl Drop for Enter { 174 | fn drop(&mut self) { 175 | CURRENT_EXECUTOR.with(|current| { 176 | if current.borrow().as_ref().is_none() { 177 | panic!("Executor not initialized") 178 | } 179 | 180 | match current.replace(None) { 181 | Some(_) => { /* Executor is dropped here nicely */ } 182 | _ => panic!("Executor already initialized"), 183 | } 184 | }) 185 | } 186 | } 187 | 188 | pub fn initialize() -> Enter { 189 | CURRENT_EXECUTOR.with(|current| { 190 | if current.borrow().as_ref().is_some() { 191 | panic!("Executor already initialized"); 192 | } 193 | 194 | let executor = CurrentThreadExecutor::new(); 195 | 196 | match current.replace(Some(executor)) { 197 | Some(_) => panic!("Executor already initialized"), 198 | _ => Enter {}, 199 | } 200 | }) 201 | } 202 | 203 | pub fn spawn(future: F) 204 | where 205 | F: Future + 'static, 206 | { 207 | let task = Rc::new(TaskHandle { 208 | task: UnsafeCell::new(Some(TaskContext::new(future))), 209 | queued: Cell::new(true), 210 | }); 211 | 212 | with_queue(|queue| queue.add_task(task)) 213 | } 214 | 215 | pub fn pure_poll() -> bool { 216 | with_default(|executor| { 217 | let mut ret = false; 218 | loop { 219 | let task_handle_ptr = match executor.tq.borrow_mut().poll_task_from_rc() { 220 | Some(rc_task_handle) => rc_task_handle, 221 | None => { 222 | return ret; 223 | } 224 | }; 225 | 226 | unsafe { 227 | let mut task = match (*(*task_handle_ptr).task.get()).take() { 228 | Some(task) => task, 229 | None => { 230 | // The future has gone away; just need to make sure 231 | // we invoke Drop on task_handle_ptr 232 | let _node = Rc::from_raw(task_handle_ptr); 233 | continue; 234 | } 235 | }; 236 | 237 | // Unset queued flag... this must be done before 238 | // polling. This ensures that the future gets 239 | // rescheduled if it is notified **during** a call 240 | // to `pure_poll`. 241 | let prev = (*task_handle_ptr).queued.replace(false); 242 | assert!(prev); 243 | 244 | ret = true; 245 | 246 | struct Bomb { 247 | task_handle: Option>, 248 | } 249 | 250 | // Bomb now owns task_handle_ptr 251 | let mut bomb = Bomb { 252 | task_handle: Some(Rc::from_raw(task_handle_ptr)), 253 | }; 254 | 255 | let res = { 256 | let waker = waker_ref::waker_ref(bomb.task_handle.as_ref().unwrap()); 257 | let mut cx = Context::from_waker(&waker); 258 | 259 | let future = Pin::new_unchecked(&mut task.fut); 260 | future.poll(&mut cx) 261 | }; 262 | 263 | if let Poll::Pending = res { 264 | // We have transferred the reference to the task to Waker 265 | // So need to move it out of Bomb and put back the task 266 | let task_handle = bomb.task_handle.take().unwrap(); 267 | 268 | *task_handle.task.get() = Some(task); 269 | 270 | // TODO: Figure out if I need to drop the task handle 271 | continue; 272 | } 273 | } 274 | } 275 | }) 276 | } 277 | 278 | fn with_default(f: F) -> R 279 | where 280 | F: FnOnce(&CurrentThreadExecutor) -> R, 281 | { 282 | CURRENT_EXECUTOR.with(|current| match *current.borrow() { 283 | Some(ref current_thread) => f(current_thread), 284 | None => panic!("Executor not set"), 285 | }) 286 | } 287 | 288 | fn with_default_no_fail(f: F) -> Result 289 | where 290 | F: FnOnce(Option<&CurrentThreadExecutor>) -> Result, 291 | { 292 | CURRENT_EXECUTOR.with(|current| match *current.borrow() { 293 | Some(ref current_thread) => f(Some(current_thread)), 294 | None => f(None), 295 | }) 296 | } 297 | 298 | fn with_queue(f: F) -> R 299 | where 300 | F: FnOnce(&mut TaskQueue) -> R, 301 | { 302 | with_default(|executor| f(&mut executor.tq.borrow_mut())) 303 | } 304 | 305 | pub fn abort(s: &str) -> ! { 306 | struct DoublePanic; 307 | 308 | impl Drop for DoublePanic { 309 | fn drop(&mut self) { 310 | //panic!("panicking twice to abort the program"); 311 | } 312 | } 313 | 314 | let _bomb = DoublePanic; 315 | panic!("{}", s); 316 | } 317 | 318 | #[cfg(test)] 319 | #[macro_use] 320 | extern crate hamcrest2; 321 | 322 | #[cfg(test)] 323 | mod tests { 324 | 325 | use super::*; 326 | use futures::task::Poll; 327 | use hamcrest2::prelude::*; 328 | use std::task::Waker; 329 | 330 | type PollerFn = dyn Fn(&Waker, &mut Controller) -> Poll<()>; 331 | 332 | struct Controller { 333 | pollers: VecDeque>, 334 | poll_count: usize, 335 | dropped: bool, 336 | waker: Option, 337 | } 338 | 339 | impl Controller { 340 | fn new() -> Controller { 341 | Controller { 342 | poll_count: 0, 343 | dropped: false, 344 | pollers: VecDeque::new(), 345 | waker: None, 346 | } 347 | } 348 | 349 | fn save_waker(&mut self, waker: &Waker) { 350 | match self.waker { 351 | Some(_) => panic!("Waker already saved"), 352 | None => self.waker = Some(waker.clone()), 353 | } 354 | } 355 | 356 | fn unwrap_waker(&mut self) -> Waker { 357 | self.waker.take().unwrap() 358 | } 359 | 360 | fn dropped(&mut self) { 361 | assert!(!self.dropped, "Already dropped"); 362 | self.dropped = true; 363 | } 364 | 365 | fn is_dropped(&self) -> bool { 366 | self.dropped 367 | } 368 | 369 | fn poll(&mut self) { 370 | self.poll_count += 1; 371 | } 372 | 373 | fn poll_count(&self) -> usize { 374 | self.poll_count 375 | } 376 | 377 | fn push_pollers

(&mut self, poller: P) 378 | where 379 | P: Fn(&Waker, &mut Controller) -> Poll<()> + 'static, 380 | { 381 | self.pollers.push_back(Box::new(poller)) 382 | } 383 | 384 | fn pop_pollers(&mut self) -> Option> { 385 | self.pollers.pop_front() 386 | } 387 | } 388 | 389 | struct MockFuture { 390 | controller: Rc>, 391 | } 392 | 393 | impl MockFuture { 394 | fn new(controller: Rc>) -> Self { 395 | MockFuture { controller } 396 | } 397 | } 398 | 399 | impl Drop for MockFuture { 400 | fn drop(&mut self) { 401 | self.controller.borrow_mut().dropped(); 402 | } 403 | } 404 | 405 | impl Future for MockFuture { 406 | type Output = (); 407 | 408 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { 409 | let mut ctrl = self.controller.borrow_mut(); 410 | let poller = match ctrl.pop_pollers() { 411 | Some(poller) => poller, 412 | None => panic!("Called poll when not expected"), 413 | }; 414 | ctrl.poll(); 415 | poller(cx.waker(), &mut ctrl) 416 | } 417 | } 418 | 419 | #[test] 420 | fn if_waker_is_not_saved_future_is_dropped() { 421 | mock_test(|ctrl| { 422 | // Return pending but do not keep a Waker reference around 423 | ctrl.borrow_mut().push_pollers(|_, _| Poll::Pending); 424 | 425 | assert_pure_poll(&ctrl, 1, true); 426 | }) 427 | } 428 | 429 | #[test] 430 | fn future_is_notified_from_outside_poll() { 431 | mock_test(|ctrl| { 432 | // Save waker 433 | ctrl.borrow_mut().push_pollers(|lw, ctrl| { 434 | ctrl.save_waker(lw); 435 | Poll::Pending 436 | }); 437 | 438 | assert_pure_poll(&ctrl, 1, false); 439 | 440 | // Notify future multiple times 441 | let waker = ctrl.borrow_mut().unwrap_waker(); 442 | waker.wake_by_ref(); 443 | waker.wake_by_ref(); 444 | 445 | // Do not save the waker this time 446 | ctrl.borrow_mut().push_pollers(|_, _| Poll::Pending); 447 | 448 | assert_pure_poll(&ctrl, 2, false); 449 | 450 | drop(waker); 451 | 452 | assert_dropped(&ctrl, true); 453 | }) 454 | } 455 | 456 | #[test] 457 | fn future_is_polled_again_if_notified_from_poll() { 458 | mock_test(|ctrl| { 459 | ctrl.borrow_mut().push_pollers(|lw, _| { 460 | // Wake multiple times 461 | lw.wake_by_ref(); 462 | lw.wake_by_ref(); 463 | 464 | Poll::Pending 465 | }); 466 | 467 | ctrl.borrow_mut().push_pollers(|_, _| Poll::Pending); 468 | 469 | assert_pure_poll(&ctrl, 2, true); 470 | }) 471 | } 472 | 473 | // TODO: Spawn a future that spawns from poll. 474 | 475 | fn mock_test(f: F) 476 | where 477 | F: Fn(Rc>), 478 | { 479 | let _enter = initialize(); 480 | 481 | let ctrl = Rc::new(RefCell::new(Controller::new())); 482 | assert_that!( 483 | LocalExecutor::new().spawn_local_obj(Box::new(MockFuture::new(ctrl.clone())).into()), 484 | is(ok()) 485 | ); 486 | 487 | f(ctrl) 488 | } 489 | 490 | fn assert_pure_poll(ctrl: &Rc>, poll_count: usize, is_dropped: bool) { 491 | pure_poll(); 492 | 493 | assert_that!(ctrl.borrow().poll_count(), is(equal_to(poll_count))); 494 | assert_dropped(ctrl, is_dropped); 495 | } 496 | 497 | fn assert_dropped(ctrl: &Rc>, is_dropped: bool) { 498 | assert_that!(ctrl.borrow().is_dropped(), is(is_dropped)); 499 | } 500 | } 501 | -------------------------------------------------------------------------------- /starfish-executor/src/waker.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::rc::Rc; 3 | use std::task::RawWaker; 4 | use std::task::RawWakerVTable; 5 | use std::task::Waker; 6 | 7 | pub fn waker(wake: Rc) -> Waker 8 | where 9 | W: RcWake, 10 | { 11 | let ptr = Rc::into_raw(wake) as *const (); 12 | 13 | unsafe { Waker::from_raw(RawWaker::new(ptr, waker_vtable!(W))) } 14 | } 15 | 16 | pub trait RcWake { 17 | fn wake(self: Rc) { 18 | Self::wake_by_ref(&self) 19 | } 20 | 21 | fn wake_by_ref(rc_self: &Rc); 22 | } 23 | 24 | // used by `waker_ref`; impl for `RcWake` 25 | pub(super) unsafe fn clone_rc_raw(data: *const ()) -> RawWaker { 26 | increase_refcount::(data); 27 | RawWaker::new(data, waker_vtable!(T)) 28 | } 29 | 30 | // impl for `RcWake` 31 | unsafe fn wake_rc_raw(data: *const ()) { 32 | let arc: Rc = Rc::from_raw(data as *const T); 33 | RcWake::wake(arc); 34 | } 35 | 36 | // used by `waker_ref`; impl for `RcWake` 37 | pub(super) unsafe fn wake_by_ref_rc_raw(data: *const ()) { 38 | let arc: Rc = Rc::from_raw(data as *const T); 39 | RcWake::wake_by_ref(&arc); 40 | mem::forget(arc); 41 | } 42 | 43 | // impl for `RcWake` 44 | unsafe fn drop_rc_raw(data: *const ()) { 45 | drop(Rc::::from_raw(data as *const T)) 46 | } 47 | 48 | // FIXME: panics on Rc::clone / refcount changes could wreak havoc on the 49 | // code here. We should guard against this by aborting. 50 | // Utility 51 | unsafe fn increase_refcount(data: *const ()) { 52 | // Retain Rc by creating a copy 53 | let rc: Rc = Rc::from_raw(data as *const T); 54 | let rc_clone = rc.clone(); 55 | // Forget the Rcs again, so that the refcount isn't decreased 56 | mem::forget(rc); 57 | mem::forget(rc_clone); 58 | } 59 | -------------------------------------------------------------------------------- /starfish-executor/src/waker_ref.rs: -------------------------------------------------------------------------------- 1 | use crate::waker::clone_rc_raw; 2 | use crate::waker::wake_by_ref_rc_raw; 3 | use crate::waker::RcWake; 4 | use std::marker::PhantomData; 5 | use std::ops::Deref; 6 | use std::rc::Rc; 7 | use std::task::RawWaker; 8 | use std::task::RawWakerVTable; 9 | use std::task::Waker; 10 | 11 | /// A [`Waker`] that is only valid for a given lifetime. 12 | /// 13 | /// Note: this type implements [`Deref`](std::ops::Deref), 14 | /// so it can be used to get a `&Waker`. 15 | #[derive(Debug)] 16 | pub struct WakerRef<'a> { 17 | waker: Waker, 18 | _marker: PhantomData<&'a ()>, 19 | } 20 | 21 | impl WakerRef<'_> { 22 | /// Create a new [`WakerRef`] from a [`Waker`]. 23 | /// 24 | /// Note: this function is safe, but it is generally only used 25 | /// from `unsafe` contexts that need to create a `Waker` 26 | /// that is guaranteed not to outlive a particular lifetime. 27 | pub fn new(waker: Waker) -> Self { 28 | WakerRef { 29 | waker, 30 | _marker: PhantomData, 31 | } 32 | } 33 | } 34 | 35 | impl Deref for WakerRef<'_> { 36 | type Target = Waker; 37 | 38 | fn deref(&self) -> &Waker { 39 | &self.waker 40 | } 41 | } 42 | 43 | #[inline] 44 | unsafe fn noop(_data: *const ()) {} 45 | 46 | unsafe fn wake_unreachable(_data: *const ()) { 47 | // With only a reference, calling `wake_rc_raw()` would be unsound, 48 | // since the `WakerRef` didn't increment the refcount of the `RcWake`, 49 | // and `wake_rc_raw` would *decrement* it. 50 | // 51 | // This should never be reachable, since `WakerRef` only provides a `Deref` 52 | // to the inner `Waker`. 53 | // 54 | // Still, safer to panic here than to call `wake_rc_raw`. 55 | unreachable!("WakerRef::wake"); 56 | } 57 | 58 | /// Creates a reference to a [`Waker`] from a reference to `Rc`. 59 | /// 60 | /// The resulting [`Waker`] will call 61 | /// [`RcWake.wake()`](RcWake::wake) if awoken. 62 | #[inline] 63 | pub fn waker_ref(wake: &Rc) -> WakerRef<'_> 64 | where 65 | W: RcWake, 66 | { 67 | // This uses the same mechanism as Arc::into_raw, without needing a reference. 68 | // This is potentially not stable 69 | #![allow(clippy::cast_ptr_alignment)] 70 | let ptr = &*wake as &W as *const W as *const (); 71 | 72 | // Similar to `waker_vtable`, but with a no-op `drop` function. 73 | // Clones of the resulting `RawWaker` will still be dropped normally. 74 | let vtable = &RawWakerVTable::new( 75 | clone_rc_raw::, 76 | wake_unreachable, 77 | wake_by_ref_rc_raw::, 78 | noop, 79 | ); 80 | 81 | let waker = unsafe { Waker::from_raw(RawWaker::new(ptr, vtable)) }; 82 | WakerRef::new(waker) 83 | } 84 | --------------------------------------------------------------------------------