├── .gitignore ├── .travis.yml ├── Cargo.lock ├── Cargo.toml ├── README.md ├── examples └── rand.rs └── src ├── error.rs ├── lib.rs └── slot.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # 2 | 3 | language: rust 4 | script: 5 | - (for i in {1..10}; do RUST_LOG=info cargo run --release --example rand || exit 1; done) -------------------------------------------------------------------------------- /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.4" 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 = "arrayvec" 13 | version = "0.4.11" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | dependencies = [ 16 | "nodrop 0.1.13 (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.60 (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 = "backtrace" 30 | version = "0.3.33" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | dependencies = [ 33 | "backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", 34 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 35 | "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", 36 | "rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", 37 | ] 38 | 39 | [[package]] 40 | name = "backtrace-sys" 41 | version = "0.1.31" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | dependencies = [ 44 | "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", 45 | "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", 46 | ] 47 | 48 | [[package]] 49 | name = "c2-chacha" 50 | version = "0.2.2" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | dependencies = [ 53 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 54 | "ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 55 | ] 56 | 57 | [[package]] 58 | name = "cc" 59 | version = "1.0.38" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | 62 | [[package]] 63 | name = "cfg-if" 64 | version = "0.1.9" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | 67 | [[package]] 68 | name = "crossbeam" 69 | version = "0.7.2" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | dependencies = [ 72 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 73 | "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 74 | "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 75 | "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", 76 | "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 77 | "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", 78 | ] 79 | 80 | [[package]] 81 | name = "crossbeam-channel" 82 | version = "0.3.9" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | dependencies = [ 85 | "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", 86 | ] 87 | 88 | [[package]] 89 | name = "crossbeam-deque" 90 | version = "0.7.1" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | dependencies = [ 93 | "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", 94 | "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", 95 | ] 96 | 97 | [[package]] 98 | name = "crossbeam-epoch" 99 | version = "0.7.2" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | dependencies = [ 102 | "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", 103 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 104 | "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", 105 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 106 | "memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 107 | "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 108 | ] 109 | 110 | [[package]] 111 | name = "crossbeam-queue" 112 | version = "0.1.2" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | dependencies = [ 115 | "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", 116 | ] 117 | 118 | [[package]] 119 | name = "crossbeam-utils" 120 | version = "0.6.6" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | dependencies = [ 123 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 124 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 125 | ] 126 | 127 | [[package]] 128 | name = "env_logger" 129 | version = "0.6.2" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | dependencies = [ 132 | "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", 133 | "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 134 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 135 | "regex 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 136 | "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 137 | ] 138 | 139 | [[package]] 140 | name = "failure" 141 | version = "0.1.5" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | dependencies = [ 144 | "backtrace 0.3.33 (registry+https://github.com/rust-lang/crates.io-index)", 145 | "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 146 | ] 147 | 148 | [[package]] 149 | name = "failure_derive" 150 | version = "0.1.5" 151 | source = "registry+https://github.com/rust-lang/crates.io-index" 152 | dependencies = [ 153 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 154 | "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", 155 | "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", 156 | "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", 157 | ] 158 | 159 | [[package]] 160 | name = "getrandom" 161 | version = "0.1.7" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | dependencies = [ 164 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 165 | "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", 166 | ] 167 | 168 | [[package]] 169 | name = "hulunbuir" 170 | version = "0.3.0" 171 | dependencies = [ 172 | "crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", 173 | "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", 174 | "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 175 | "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 176 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 177 | "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 178 | ] 179 | 180 | [[package]] 181 | name = "humantime" 182 | version = "1.2.0" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | dependencies = [ 185 | "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 186 | ] 187 | 188 | [[package]] 189 | name = "lazy_static" 190 | version = "1.3.0" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | 193 | [[package]] 194 | name = "libc" 195 | version = "0.2.60" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | 198 | [[package]] 199 | name = "log" 200 | version = "0.4.8" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | dependencies = [ 203 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 204 | ] 205 | 206 | [[package]] 207 | name = "memchr" 208 | version = "2.2.1" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | 211 | [[package]] 212 | name = "memoffset" 213 | version = "0.5.1" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | dependencies = [ 216 | "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 217 | ] 218 | 219 | [[package]] 220 | name = "nodrop" 221 | version = "0.1.13" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | 224 | [[package]] 225 | name = "ppv-lite86" 226 | version = "0.2.5" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | 229 | [[package]] 230 | name = "proc-macro2" 231 | version = "0.4.30" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | dependencies = [ 234 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 235 | ] 236 | 237 | [[package]] 238 | name = "quick-error" 239 | version = "1.2.2" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | 242 | [[package]] 243 | name = "quote" 244 | version = "0.6.13" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | dependencies = [ 247 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 248 | ] 249 | 250 | [[package]] 251 | name = "rand" 252 | version = "0.7.0" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | dependencies = [ 255 | "getrandom 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 256 | "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", 257 | "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 258 | "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", 259 | "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 260 | ] 261 | 262 | [[package]] 263 | name = "rand_chacha" 264 | version = "0.2.1" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | dependencies = [ 267 | "c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 268 | "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", 269 | ] 270 | 271 | [[package]] 272 | name = "rand_core" 273 | version = "0.5.0" 274 | source = "registry+https://github.com/rust-lang/crates.io-index" 275 | dependencies = [ 276 | "getrandom 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 277 | ] 278 | 279 | [[package]] 280 | name = "rand_hc" 281 | version = "0.2.0" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | dependencies = [ 284 | "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", 285 | ] 286 | 287 | [[package]] 288 | name = "regex" 289 | version = "1.2.0" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | dependencies = [ 292 | "aho-corasick 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", 293 | "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 294 | "regex-syntax 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", 295 | "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 296 | "utf8-ranges 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 297 | ] 298 | 299 | [[package]] 300 | name = "regex-syntax" 301 | version = "0.6.10" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | dependencies = [ 304 | "ucd-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 305 | ] 306 | 307 | [[package]] 308 | name = "rustc-demangle" 309 | version = "0.1.15" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | 312 | [[package]] 313 | name = "rustc_version" 314 | version = "0.2.3" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | dependencies = [ 317 | "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", 318 | ] 319 | 320 | [[package]] 321 | name = "scopeguard" 322 | version = "1.0.0" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | 325 | [[package]] 326 | name = "semver" 327 | version = "0.9.0" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | dependencies = [ 330 | "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 331 | ] 332 | 333 | [[package]] 334 | name = "semver-parser" 335 | version = "0.7.0" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | 338 | [[package]] 339 | name = "syn" 340 | version = "0.15.42" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | dependencies = [ 343 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 344 | "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", 345 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 346 | ] 347 | 348 | [[package]] 349 | name = "synstructure" 350 | version = "0.10.2" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | dependencies = [ 353 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 354 | "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", 355 | "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", 356 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 357 | ] 358 | 359 | [[package]] 360 | name = "termcolor" 361 | version = "1.0.5" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | dependencies = [ 364 | "wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 365 | ] 366 | 367 | [[package]] 368 | name = "thread_local" 369 | version = "0.3.6" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | dependencies = [ 372 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 373 | ] 374 | 375 | [[package]] 376 | name = "ucd-util" 377 | version = "0.1.5" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | 380 | [[package]] 381 | name = "unicode-xid" 382 | version = "0.1.0" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | 385 | [[package]] 386 | name = "utf8-ranges" 387 | version = "1.0.3" 388 | source = "registry+https://github.com/rust-lang/crates.io-index" 389 | 390 | [[package]] 391 | name = "winapi" 392 | version = "0.3.7" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | dependencies = [ 395 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 396 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 397 | ] 398 | 399 | [[package]] 400 | name = "winapi-i686-pc-windows-gnu" 401 | version = "0.4.0" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | 404 | [[package]] 405 | name = "winapi-util" 406 | version = "0.1.2" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | dependencies = [ 409 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 410 | ] 411 | 412 | [[package]] 413 | name = "winapi-x86_64-pc-windows-gnu" 414 | version = "0.4.0" 415 | source = "registry+https://github.com/rust-lang/crates.io-index" 416 | 417 | [[package]] 418 | name = "wincolor" 419 | version = "1.0.1" 420 | source = "registry+https://github.com/rust-lang/crates.io-index" 421 | dependencies = [ 422 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 423 | "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 424 | ] 425 | 426 | [metadata] 427 | "checksum aho-corasick 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "36b7aa1ccb7d7ea3f437cf025a2ab1c47cc6c1bc9fc84918ff449def12f5e282" 428 | "checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" 429 | "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" 430 | "checksum backtrace 0.3.33 (registry+https://github.com/rust-lang/crates.io-index)" = "88fb679bc9af8fa639198790a77f52d345fe13656c08b43afa9424c206b731c6" 431 | "checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b" 432 | "checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" 433 | "checksum cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)" = "ce400c638d48ee0e9ab75aef7997609ec57367ccfe1463f21bf53c3eca67bf46" 434 | "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" 435 | "checksum crossbeam 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2d818a4990769aac0c7ff1360e233ef3a41adcb009ebb2036bf6915eb0f6b23c" 436 | "checksum crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa" 437 | "checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" 438 | "checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" 439 | "checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" 440 | "checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" 441 | "checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" 442 | "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" 443 | "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" 444 | "checksum getrandom 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8e190892c840661957ba9f32dacfb3eb405e657f9f9f60485605f0bb37d6f8" 445 | "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" 446 | "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" 447 | "checksum libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "d44e80633f007889c7eff624b709ab43c92d708caad982295768a7b13ca3b5eb" 448 | "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" 449 | "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" 450 | "checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f" 451 | "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" 452 | "checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" 453 | "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" 454 | "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" 455 | "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" 456 | "checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c" 457 | "checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" 458 | "checksum rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "615e683324e75af5d43d8f7a39ffe3ee4a9dc42c5c701167a71dc59c3a493aca" 459 | "checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 460 | "checksum regex 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6b23da8dfd98a84bd7e08700190a5d9f7d2d38abd4369dd1dae651bc40bfd2cc" 461 | "checksum regex-syntax 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "cd5485bf1523a9ed51c4964273f22f63f24e31632adb5dad134f488f86a3875c" 462 | "checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af" 463 | "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 464 | "checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" 465 | "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 466 | "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 467 | "checksum syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)" = "eadc09306ca51a40555dd6fc2b415538e9e18bc9f870e47b1a524a79fe2dcf5e" 468 | "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" 469 | "checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e" 470 | "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" 471 | "checksum ucd-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa9b3b49edd3468c0e6565d85783f51af95212b6fa3986a5500954f00b460874" 472 | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 473 | "checksum utf8-ranges 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9d50aa7650df78abf942826607c62468ce18d9019673d4a2ebe1865dbb96ffde" 474 | "checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" 475 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 476 | "checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" 477 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 478 | "checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba" 479 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hulunbuir" 3 | version = "0.3.0" 4 | authors = ["whoiscc "] 5 | edition = "2018" 6 | 7 | description = "General purpose cross-thread GC" 8 | repository = "https://github.com/whoiscc/hulunbuir" 9 | readme = "README.md" 10 | license = "MIT" 11 | 12 | [dependencies] 13 | crossbeam = "^0.7.2" 14 | failure = "^0.1.5" 15 | failure_derive = "^0.1.1" 16 | log = "^0.4.8" 17 | 18 | [dev-dependencies] 19 | rand = "^0.7" 20 | env_logger = "^0.6.2" 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hulunbuir 2 | 3 | [![Build Status](https://travis-ci.com/whoiscc/hulunbuir.svg?branch=master)](https://travis-ci.com/whoiscc/hulunbuir) 4 | [![Doc](https://docs.rs/hulunbuir/badge.svg)](https://docs.rs/hulunbuir) 5 | 6 | See `examples` folder for basic usage. -------------------------------------------------------------------------------- /examples/rand.rs: -------------------------------------------------------------------------------- 1 | // 2 | 3 | use std::collections::HashMap; 4 | use std::mem; 5 | use std::sync::{Arc, Mutex}; 6 | use std::thread; 7 | 8 | use hulunbuir::{ 9 | slot::{Slot, Take}, 10 | Address, Collector, Keep, 11 | }; 12 | 13 | use env_logger; 14 | use rand::{thread_rng, Rng}; 15 | 16 | struct Node { 17 | children: Vec
, 18 | locked: HashMap, 19 | } 20 | 21 | impl Keep for Node { 22 | fn with_keep(&self, f: F) { 23 | let union: Vec<_> = self 24 | .children 25 | .iter() 26 | .chain(self.locked.keys()) 27 | .cloned() 28 | .collect(); 29 | let _: Vec<_> = union.iter().map(f).collect(); 30 | } 31 | } 32 | 33 | impl Node { 34 | fn new() -> Self { 35 | Node { 36 | children: Vec::new(), 37 | locked: HashMap::new(), 38 | } 39 | } 40 | 41 | fn lock(&mut self, address: &Address) { 42 | if self.locked.contains_key(address) { 43 | *self.locked.get_mut(address).unwrap() += 1; 44 | } else { 45 | self.locked.insert(address.clone(), 1); 46 | } 47 | } 48 | 49 | fn unlock(&mut self, address: &Address) { 50 | // println!("unlocking"); 51 | *self.locked.get_mut(address).unwrap() -= 1; 52 | if *self.locked.get(address).unwrap() == 0 { 53 | // println!("removing"); 54 | self.locked.remove(address); 55 | } 56 | } 57 | } 58 | 59 | fn wait(collector: &Mutex>>, address: &Address) -> Node { 60 | loop { 61 | let result = collector.lock().unwrap().take(address).unwrap(); 62 | match result { 63 | Take::Free(node) => return node, 64 | Take::Busy(parker) => parker.park(), 65 | } 66 | } 67 | } 68 | 69 | fn main() { 70 | env_logger::init(); 71 | 72 | let collector = Arc::new(Mutex::new(Collector::new(4096))); 73 | let root = collector 74 | .lock() 75 | .unwrap() 76 | .allocate(Slot::new(Node::new())) 77 | .unwrap(); 78 | collector.lock().unwrap().set_root(root.clone()); 79 | let mut handle: [Option>; 10] = Default::default(); 80 | for i in 0..10 { 81 | let thread_collector = Arc::clone(&collector); 82 | let thread_root = root.clone(); 83 | handle[i] = Some(thread::spawn(move || { 84 | let mut rng = thread_rng(); 85 | let collector = thread_collector; 86 | let root = thread_root; 87 | for _j in 0..16384 { 88 | let mut current = root.clone(); 89 | let mut node; 90 | let mut node_stack = Vec::new(); 91 | loop { 92 | // println!("start loop"); 93 | node = wait(&collector, ¤t); 94 | let stop = node.children.is_empty() || rng.gen::() < 0.05; 95 | if stop { 96 | // current node is still used outside loop block 97 | // so it is not filled before breaking 98 | // go to hell RAII 99 | break; 100 | } 101 | let child_index = rng.gen_range(0, node.children.len()); 102 | let next_current = node.children[child_index].to_owned(); 103 | node.lock(&next_current); 104 | collector.lock().unwrap().fill(¤t, node).unwrap(); 105 | node_stack.push(current.clone()); 106 | current = next_current; 107 | } 108 | let replaced_child = rng.gen_range(0, 100); 109 | // mutex lock is saved for reusing here 110 | // otherwise, other thread may trigger a collecting between 111 | // allocation of new object and filling its parent 112 | // which will collect the new object immediately 113 | let mut new_child_write = collector.lock().unwrap(); 114 | let new_child = new_child_write.allocate(Slot::new(Node::new())).unwrap(); 115 | if node.children.len() <= replaced_child { 116 | node.children.push(new_child); 117 | } else { 118 | node.children[replaced_child] = new_child; 119 | } 120 | new_child_write.fill(¤t, node).unwrap(); 121 | mem::drop(new_child_write); 122 | while let Some(parent) = node_stack.pop() { 123 | let mut node = wait(&collector, &parent); 124 | node.unlock(¤t); 125 | collector.lock().unwrap().fill(&parent, node).unwrap(); 126 | current = parent; 127 | } 128 | } 129 | })); 130 | } 131 | for i in 0..10 { 132 | handle[i].take().unwrap().join().unwrap(); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | 2 | /// Errors thrown by collector. 3 | #[derive(Debug, Fail)] 4 | pub enum Error { 5 | /// Alive objects count reaches `slot_max` passed to `Collector::new`, and no object 6 | /// is collectable. 7 | #[fail(display = "out of slots")] 8 | OutOfSlots, 9 | /// Trying to access object with invalid address. 10 | #[fail(display = "invalid address")] 11 | InvalidAddress, 12 | /// Calling `Collector::fill` on non-empty slot. See document of `slot` module for details. 13 | #[fail(display = "duplicated filling")] 14 | DuplicatedFilling, 15 | } -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Hulunbuir is a cross-thread garbage collector. The managed objects could be used in 2 | //! multithreads, and collecting process may happen in any of them. 3 | //! 4 | //! Normally, reading or updating a managed object must lock global collector as well, 5 | //! which significantly decrease multithread performance. However, Hulunbuir does not provide 6 | //! common "read guard" and "write guard" interface; instead it only supports two functions: 7 | //! `allocate` and `replace`. The first one create a managed object, and may trigger a garbage 8 | //! collecting process if necessary; the second one replace the value of a managed object with 9 | //! a new one provided by argument. The global collector only have to be locked during replacing 10 | //! and the lock could be released when working thread owns the value. So the lock will not 11 | //! become the bottleneck of performance. 12 | //! 13 | //! Hulunbuir also provides `Slot` as higher level abstraction and interface. 14 | //! 15 | //! # Basic usage 16 | //! 17 | //! ``` 18 | //! use hulunbuir::{Address, Collector, Keep}; 19 | //! 20 | //! // create a managed type 21 | //! struct ListNode(i32, Option
); 22 | //! 23 | //! // implement Keep for it, so it could be managed 24 | //! impl Keep for ListNode { 25 | //! fn with_keep(&self, mut keep: F) { 26 | //! // each node keeps only its tail, so call `keep` with it... 27 | //! if let Some(tail) = &self.1 { 28 | //! // ...if the node has tail 29 | //! keep(tail) 30 | //! } 31 | //! } 32 | //! } 33 | //! 34 | //! fn main() { 35 | //! // create a collector with 128 slots available 36 | //! let mut collector = Collector::new(128); 37 | //! let root = collector.allocate(ListNode(0, None)).unwrap(); 38 | //! collector.set_root(root.clone()); 39 | //! let tail = collector.allocate(ListNode(1, None)).unwrap(); 40 | //! // replace root node out with something not important 41 | //! let mut root_node = collector.replace(&root, ListNode(42, None)).unwrap(); 42 | //! root_node.1 = Some(tail); 43 | //! // replace root node back 44 | //! let _ = collector.replace(&root, root_node).unwrap(); 45 | //! 46 | //! let _orphan = collector.allocate(ListNode(2, None)).unwrap(); 47 | //! // before collecting... 48 | //! assert_eq!(collector.alive_count(), 3); 49 | //! collector.collect(); 50 | //! // after collecting... 51 | //! assert_eq!(collector.alive_count(), 2); 52 | //! } 53 | //! ``` 54 | //! 55 | //! This `replace`-based object updating strategy is suitable for simple single-thread usage. 56 | //! The collector will work correctly **only when no garbage collection happens when any 57 | //! "real" object is replaced out**, which means, when any of them *is* replaced out: 58 | //! * no explicit calling to `Collector::collect` 59 | //! * no calling to `Collector::allocate`, since it may trigger collection as well if there's 60 | //! no slot available 61 | //! 62 | //! In multithreading context, none of above could be archieved since each thread has no idea 63 | //! about what the others are doing. So more complicated strategy must be introduced. Hulunbuir 64 | //! provides `slot` module for this purpose, but you are free to develop your own one. 65 | 66 | /// Errors. 67 | pub mod error; 68 | /// Slot-based abstraction for automatic dependency caching and thread parking. 69 | pub mod slot; 70 | 71 | use std::collections::HashMap; 72 | use std::mem; 73 | use std::time::Instant; 74 | 75 | pub use crate::error::Error; 76 | 77 | #[macro_use] 78 | extern crate failure_derive; 79 | 80 | use log::info; 81 | 82 | /// Memory manager for allocation and garbage collection. 83 | /// 84 | /// See module level document for basic usage. 85 | #[derive(Debug)] 86 | pub struct Collector { 87 | slots: HashMap>, 88 | slot_max: usize, 89 | next_id: usize, 90 | root: Option
, 91 | } 92 | 93 | /// Virtual memory address token. 94 | #[derive(Hash, PartialEq, Eq, Clone, Debug)] 95 | pub struct Address(usize); 96 | 97 | /// Required trait for managed objects' type. 98 | pub trait Keep { 99 | /// When this method is called, it should calls back `keep` with the addresses of objects 100 | /// that this object wishes to keep, one per calling. If current object is considered 101 | /// as alive in a garbage collecting pass (probably since this method is called), then 102 | /// all the kept objects will also be considered as alive. 103 | /// 104 | /// If this method is not implemented properly, such as not calling `keep` or calling it 105 | /// with insufficient addresses, `Memory::InvalidAddress` may be thrown in arbitrary time 106 | /// in the future. 107 | /// 108 | /// There's no reason for this method to fail. Please panic if you have to. 109 | fn with_keep(&self, keep: F); 110 | } 111 | 112 | impl Collector { 113 | /// Create a collector with `slot_max` slots available. Each slot is able to store a managed 114 | /// object typed `T`. 115 | pub fn new(slot_max: usize) -> Self { 116 | Self { 117 | slots: HashMap::new(), 118 | slot_max, 119 | next_id: 0, 120 | root: None, 121 | } 122 | } 123 | 124 | /// Replace the value of object at `address` with `value`. Return the original value of 125 | /// managed object. If there's no object at `address` (maybe the object there has been 126 | /// collected), throw `Error::InvalidAddress`. 127 | pub fn replace(&mut self, address: &Address, value: T) -> Result { 128 | let slot = self.slots.get_mut(address).ok_or(Error::InvalidAddress)?; 129 | let content = mem::replace(&mut slot.content, value); 130 | Ok(content) 131 | } 132 | 133 | /// Set object at `address` as root object. Only root object and objects kept by any 134 | /// object that has been considered as alive object in the current collecting pass 135 | /// will stay alive during garbage collection. 136 | pub fn set_root(&mut self, address: Address) { 137 | self.root = Some(address); 138 | } 139 | 140 | /// Return current root object. If no root object is set, return `None`, and every object 141 | /// will be collected if a collecting pass is triggered. 142 | pub fn root(&self) -> &Option
{ 143 | &self.root 144 | } 145 | 146 | /// Return the total number of managed objects. Some of them may already be dead and will 147 | /// be collected in the following garbage collection. 148 | pub fn alive_count(&self) -> usize { 149 | self.slots.len() 150 | } 151 | } 152 | 153 | #[derive(Debug)] 154 | struct Slot { 155 | mark: bool, 156 | content: T, 157 | } 158 | 159 | impl Collector { 160 | /// Create a new managed object with `value`. If there's no available slot a garbage 161 | /// collecting pass will be triggered. If there's still no available slot then 162 | /// `Error::OutOfSlot` will be thrown. Any error thrown by collecting process 163 | /// will be re-thrown. 164 | pub fn allocate(&mut self, value: T) -> Result { 165 | if self.slots.len() == self.slot_max { 166 | self.collect()?; 167 | } 168 | if self.slots.len() == self.slot_max { 169 | return Err(Error::OutOfSlots); 170 | } 171 | let address = Address(self.next_id); 172 | self.next_id += 1; 173 | self.slots.insert( 174 | address.clone(), 175 | Slot { 176 | mark: false, 177 | content: value, 178 | }, 179 | ); 180 | Ok(address) 181 | } 182 | 183 | /// Clean up all dead objects, which are unreachable from root object, or all objects 184 | /// if the root object is not set. If root object address is invalid, or any alive object 185 | /// keeps an object at invalid address, then `Memory::InvalidAddress` will be thrown. 186 | /// 187 | /// This method will be invoked if `Collector::allocate` is called but no slot is available, 188 | /// but it could also be explicit called by user. Statistics log will be printed after 189 | /// each collecting pass. 190 | pub fn collect(&mut self) -> Result<(), Error> { 191 | let start = Instant::now(); 192 | 193 | let mut stack = Vec::new(); 194 | if let Some(address) = &self.root { 195 | stack.push(address.to_owned()); 196 | } 197 | while let Some(address) = stack.pop() { 198 | let slot = self.slots.get_mut(&address).ok_or(Error::InvalidAddress)?; 199 | if slot.mark { 200 | continue; 201 | } 202 | slot.mark = true; 203 | slot.content.with_keep(|address| { 204 | stack.push(address.to_owned()); 205 | }); 206 | } 207 | let mut alive_slots = HashMap::new(); 208 | for (address, slot) in mem::replace(&mut self.slots, HashMap::new()).into_iter() { 209 | if slot.mark { 210 | alive_slots.insert( 211 | address, 212 | Slot { 213 | mark: false, 214 | content: slot.content, 215 | }, 216 | ); 217 | } 218 | } 219 | self.slots = alive_slots; 220 | 221 | info!( 222 | target: "hulunbuir", 223 | "garbage collected in {} ms, {:.2}% of available slots used", 224 | start.elapsed().as_micros() as f32 / 1000.0, 225 | self.slots.len() as f32 / self.slot_max as f32 * 100.0 226 | ); 227 | Ok(()) 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /src/slot.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! First of all, the "slot" below is not the same as the slot I mentioned in main module. 3 | //! Theoretically, instances of any type which implements `Keep` trait could be inserted 4 | //! into the slots of a collector, and the `Slot` type provided by this module is only one 5 | //! of them. However, there's real benefit to use it instead of some random types. 6 | //! 7 | //! # Multithreading in action 8 | //! 9 | //! Suppose you are migrating the list type from main module's example into multithreading 10 | //! environment. You need to handle at least two problems properly to prevent everything 11 | //! from crashing: 12 | //! * What if one thread triggers garbage collection when some of the objects are replaced 13 | //! with some fake ones by other threads? 14 | //! * What if one thread tries to replace out a managed object, which is currently replaced 15 | //! out by another thread? 16 | //! 17 | //! Fortunately, you can solve both the problems by replacing `Collector` with 18 | //! `Collector>`, and use `take` and `fill` methods instead of `replace`. 19 | //! 20 | //! # First sight in `Slot` 21 | //! 22 | //! We could rewrite the example in main module with `Slot` like this: 23 | //! 24 | //! ```rust 25 | //! use hulunbuir::{Address, Collector, Keep}; 26 | //! use hulunbuir::slot::{Slot, Take}; 27 | //! 28 | //! // exactly same type as before 29 | //! struct ListNode(i32, Option
); 30 | //! 31 | //! impl Keep for ListNode { 32 | //! fn with_keep(&self, mut keep: F) { 33 | //! if let Some(tail) = &self.1 { 34 | //! keep(tail) 35 | //! } 36 | //! } 37 | //! } 38 | //! 39 | //! fn main() { 40 | //! let mut collector = Collector::new(128); 41 | //! // allocate a Slot instead of ListNode 42 | //! let root = collector.allocate(Slot::new(ListNode(0, None))).unwrap(); 43 | //! collector.set_root(root.clone()); 44 | //! let tail = collector.allocate(Slot::new(ListNode(1, None))).unwrap(); 45 | //! // take root object out of slot, and leave a "hole" there 46 | //! let mut root_node = match collector.take(&root).unwrap() { 47 | //! Take::Free(object) => object, 48 | //! Take::Busy(_) => unreachable!(), // we know that no one is using it 49 | //! }; 50 | //! root_node.1 = Some(tail); 51 | //! // fill the hole with updated object 52 | //! collector.fill(&root, root_node).unwrap(); 53 | //! 54 | //! // the rest part is the same as before 55 | //! let _orphan = collector.allocate(Slot::new(ListNode(2, None))).unwrap(); 56 | //! assert_eq!(collector.alive_count(), 3); 57 | //! collector.collect(); 58 | //! assert_eq!(collector.alive_count(), 2); 59 | //! } 60 | //!``` 61 | //! 62 | //! By taking object out of slot, `Slot` automatically: 63 | //! * Provides a "hole" object to prevent the following threads taking it, or, to make them 64 | //! realizing that some other one is taking it 65 | //! * Calls `Keep::with_keep` method of the object before giving it out, and caches the 66 | //! result in the hole. So the hole could "pretend" to be the taken object if garbage 67 | //! collection happens. 68 | //! 69 | //! Please pay extra attention to the second function. It means **collector will not be aware of 70 | //! any change to the kept list of taken object until it is filled back**. So, if you are doing 71 | //! something like this: 72 | //! 1. lock the collector, allocate a new object, unlock it 73 | //! 2. (lock) take an object out (unlock) and make it keeping the new object 74 | //! 3. lock the collector, fill the object, unlock it 75 | //! 76 | //! Then you will get chance to lose your new object unexpectedly, if some other thread 77 | //! triggers a garbage collection while your thread is in the second stage. The correct 78 | //! way is to hold the lock through all three stages. 79 | //! 80 | //! # Blocking on taking 81 | //! 82 | //! In most of the time, when we trying to take an object out but someone else is using it, 83 | //! all we want to do is just waiting. However, the `take` method returns immediately, to 84 | //! prevent current thread holding the global collector too long. In addition to trying again 85 | //! and again as a spin lock, you can leverage on the other variant of `Take`: 86 | //! 87 | //! ```rust 88 | //! # use std::sync::Mutex; 89 | //! # use hulunbuir::{Address, Collector, Keep}; 90 | //! # use hulunbuir::slot::{Slot, Take}; 91 | //! 92 | //! fn wait(collector: &Mutex>>, address: &Address) -> T { 93 | //! loop { 94 | //! let take = collector.lock().unwrap().take(address).unwrap(); 95 | //! match take { 96 | //! Take::Free(value) => return value, 97 | //! Take::Busy(parker) => parker.park(), 98 | //! } 99 | //! } 100 | //! } 101 | //! 102 | //! # fn main() {} 103 | //! ``` 104 | //! 105 | //! The `parker` is a [`crossbeam::sync::Parker`][1]. By calling its `park` method, current 106 | //! thread will be blocked until the paired `Unparker::unpark` is called, which will be done 107 | //! by `Slot::fill`. Notice that it's not trivial to extract `take` variable out of `match` 108 | //! block, so that the mutex could be released before current thread is parked which will 109 | //! become a dead lock. 110 | //! 111 | //! [1]: https://docs.rs/crossbeam/0.7.2/crossbeam/sync/struct.Parker.html 112 | //! 113 | //! The `wait` function above may be idiomatic, but I cannot find a way to provide it because 114 | //! I have no idea what kind of mutex you prefer. 115 | //! 116 | //! # Disadvantage on using `Slot` 117 | //! 118 | //! The first disadvantage is that you cannot concurrent read an object in an obvious way. 119 | //! Certainly you can absolutely perform concurrent reading with something like 120 | //! 121 | //! > `Arc>>>>>` 122 | //! 123 | //! As we all know it turns out that Rust is all about adding another layer. 124 | //! 125 | //! The second disadvantage, which is absolutely not limited to `Slot`, is that objects must 126 | //! be moved back and forth again and again which may hurt performance seriously. This can also 127 | //! be prevented by adding a `Box` layer (what I just say?). At the very end Hulunbuir does not 128 | //! concern much about memory location right now. Maybe some day I will write a new add-on 129 | //! like `Slot` for it! 130 | //! 131 | 132 | use crate::{error::Error, Address, Collector, Keep}; 133 | 134 | use crossbeam::sync::{Parker as ParkerPriv, Unparker}; 135 | 136 | pub type Parker = ParkerPriv; 137 | 138 | enum SlotPriv { 139 | Free(T), 140 | Busy { 141 | keep: Vec
, 142 | unparkers: Vec, 143 | }, 144 | } 145 | 146 | /// A managable type which provides some more functionality. 147 | /// 148 | /// See module level document for more detail. 149 | pub struct Slot(SlotPriv); 150 | 151 | impl Slot { 152 | /// Create a new slot with `value`. 153 | pub fn new(value: T) -> Self { 154 | Self(SlotPriv::Free(value)) 155 | } 156 | } 157 | 158 | impl Keep for Slot { 159 | fn with_keep(&self, mut f: F) { 160 | match &self.0 { 161 | SlotPriv::Free(value) => value.with_keep(f), 162 | SlotPriv::Busy { keep, .. } => { 163 | for address in keep { 164 | f(address); 165 | } 166 | } 167 | } 168 | } 169 | } 170 | 171 | /// The result of trying to take an object out. 172 | pub enum Take { 173 | /// The object is not in used. 174 | Free(T), 175 | /// The object is currently used by others. You could block current thread until it 176 | /// is returned by calling `Parker::park`. 177 | Busy(Parker), 178 | } 179 | 180 | impl Collector> { 181 | /// Take the object at `address` out and leave a hole there. `Error::InvalidAddress` 182 | /// will be thrown if there's no alive object at `address`. 183 | pub fn take(&mut self, address: &Address) -> Result, Error> { 184 | let mut keep = Vec::new(); 185 | match &mut self 186 | .slots 187 | .get_mut(&address) 188 | .ok_or(Error::InvalidAddress)? 189 | .content 190 | .0 191 | { 192 | SlotPriv::Free(value) => value.with_keep(|address| keep.push(address.to_owned())), 193 | SlotPriv::Busy { unparkers, .. } => { 194 | let parker = Parker::new(); 195 | unparkers.push(parker.unparker().to_owned()); 196 | return Ok(Take::Busy(parker)); 197 | } 198 | } 199 | let busy = Slot(SlotPriv::Busy { 200 | keep, 201 | unparkers: Vec::new(), 202 | }); 203 | match self.replace(address, busy)?.0 { 204 | SlotPriv::Free(value) => Ok(Take::Free(value)), 205 | _ => unreachable!(), 206 | } 207 | } 208 | } 209 | 210 | impl Collector> { 211 | /// Fill the hole at `address` with `value`. If the address does not contain a hole of 212 | /// an alive object, `Error::InvalidAddress` will be thrown. If there is already a not-in-used 213 | /// object at `address`, then `Error::DuplicatedFilling` will be thrown. 214 | pub fn fill(&mut self, address: &Address, value: T) -> Result<(), Error> { 215 | match self.replace(address, Slot(SlotPriv::Free(value)))?.0 { 216 | SlotPriv::Free(_) => Err(Error::DuplicatedFilling), 217 | SlotPriv::Busy { unparkers, .. } => { 218 | for unparker in unparkers { 219 | unparker.unpark(); 220 | } 221 | Ok(()) 222 | } 223 | } 224 | } 225 | } 226 | --------------------------------------------------------------------------------