|
27 | 27 | # endif |
28 | 28 | # endif |
29 | 29 |
|
| 30 | +/** |
| 31 | + * In order to properly cleanup our pagemap reservation, |
| 32 | + * we need to make sure we do it is done after all other |
| 33 | + * allocations have been freed. |
| 34 | + * |
| 35 | + * One way to guarantee that the reservations get released |
| 36 | + * at the absolute end of the program is to force them to |
| 37 | + * be initialized first. Statics and globals get destroyed |
| 38 | + * in FILO order of when they were initialized. The pragma |
| 39 | + * init_seg makes sure the statics and globals in this |
| 40 | + * file are handled first, and thus will be the last to |
| 41 | + * be destroyed when the program exits or the DLL is |
| 42 | + * unloaded. |
| 43 | + */ |
| 44 | +# pragma warning(disable : 4075) |
| 45 | +# pragma init_seg(".CRT$XCB") |
| 46 | + |
30 | 47 | namespace snmalloc |
31 | 48 | { |
32 | 49 | class PALWindows : public PalTimerDefaultImpl<PALWindows> |
@@ -273,42 +290,10 @@ namespace snmalloc |
273 | 290 |
|
274 | 291 | # ifdef PLATFORM_HAS_VIRTUALALLOC2 |
275 | 292 | template<bool state_using> |
276 | | - static void* reserve_aligned(size_t size) noexcept |
277 | | - { |
278 | | - SNMALLOC_ASSERT(bits::is_pow2(size)); |
279 | | - SNMALLOC_ASSERT(size >= minimum_alloc_size); |
280 | | - |
281 | | - DWORD flags = MEM_RESERVE; |
282 | | - |
283 | | - if (state_using) |
284 | | - flags |= MEM_COMMIT; |
285 | | - |
286 | | - // If we're on Windows 10 or newer, we can use the VirtualAlloc2 |
287 | | - // function. The FromApp variant is useable by UWP applications and |
288 | | - // cannot allocate executable memory. |
289 | | - MEM_ADDRESS_REQUIREMENTS addressReqs = {NULL, NULL, size}; |
290 | | - |
291 | | - MEM_EXTENDED_PARAMETER param = { |
292 | | - {MemExtendedParameterAddressRequirements, 0}, {0}}; |
293 | | - // Separate assignment as MSVC doesn't support .Pointer in the |
294 | | - // initialisation list. |
295 | | - param.Pointer = &addressReqs; |
296 | | - |
297 | | - void* ret = VirtualAlloc2FromApp( |
298 | | - nullptr, nullptr, size, flags, PAGE_READWRITE, ¶m, 1); |
299 | | - if (ret == nullptr) |
300 | | - errno = ENOMEM; |
301 | | - return ret; |
302 | | - } |
| 293 | + static void* reserve_aligned(size_t size) noexcept; |
303 | 294 | # endif |
304 | 295 |
|
305 | | - static void* reserve(size_t size) noexcept |
306 | | - { |
307 | | - void* ret = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE); |
308 | | - if (ret == nullptr) |
309 | | - errno = ENOMEM; |
310 | | - return ret; |
311 | | - } |
| 296 | + static void* reserve(size_t size) noexcept; |
312 | 297 |
|
313 | 298 | /** |
314 | 299 | * Source of Entropy |
@@ -370,5 +355,262 @@ namespace snmalloc |
370 | 355 | } |
371 | 356 | # endif |
372 | 357 | }; |
| 358 | + |
| 359 | + /** |
| 360 | + * This VirtualVector class is an implementation of |
| 361 | + * a vector, but does not use new/malloc or any other |
| 362 | + * STL containers. We cannot use these after |
| 363 | + * init_seg(".CRT$XCB") usage, because the CRT |
| 364 | + * might not be fully initialized yet. The segments |
| 365 | + * compiler, lib, user cannot be used because of |
| 366 | + * CRT internals like std::locale facets |
| 367 | + */ |
| 368 | + class VirtualVector |
| 369 | + { |
| 370 | + void** data = nullptr; |
| 371 | + size_t size = 0; |
| 372 | + size_t committed_elements = 0; |
| 373 | + size_t reserved_elements = 0; |
| 374 | + |
| 375 | + static constexpr size_t MinCommit = |
| 376 | + snmalloc::PALWindows::page_size / sizeof(void*); |
| 377 | + static constexpr size_t MinReserve = |
| 378 | + 16 * snmalloc::PALWindows::page_size / sizeof(void*); |
| 379 | + |
| 380 | + // Lock for the reserved ranges. |
| 381 | + inline static snmalloc::FlagWord push_back_lock{}; |
| 382 | + |
| 383 | + public: |
| 384 | + VirtualVector( |
| 385 | + size_t reserve_elems = MinReserve, size_t initial_commit = MinCommit) |
| 386 | + { |
| 387 | + reserve_and_commit(reserve_elems, initial_commit); |
| 388 | + } |
| 389 | + |
| 390 | + ~VirtualVector() |
| 391 | + { |
| 392 | + if (data) |
| 393 | + { |
| 394 | + for (size_t i = size; i > 0; i--) |
| 395 | + { |
| 396 | + size_t index = i - 1; |
| 397 | + if (data[index] == nullptr) |
| 398 | + continue; |
| 399 | + |
| 400 | + BOOL ok = VirtualFree(data[index], 0, MEM_RELEASE); |
| 401 | + |
| 402 | + data[index] = nullptr; |
| 403 | + |
| 404 | + if (!ok) |
| 405 | + { |
| 406 | + snmalloc::PALWindows::error("VirtualFree failed"); |
| 407 | + } |
| 408 | + } |
| 409 | + |
| 410 | + BOOL ok = VirtualFree(data, 0, MEM_RELEASE); |
| 411 | + |
| 412 | + data = nullptr; |
| 413 | + |
| 414 | + if (!ok) |
| 415 | + { |
| 416 | + snmalloc::PALWindows::error("VirtualFree failed"); |
| 417 | + } |
| 418 | + } |
| 419 | + } |
| 420 | + |
| 421 | + void push_back(void* value) |
| 422 | + { |
| 423 | + snmalloc::FlagLock lock(push_back_lock); |
| 424 | + ensure_capacity(); |
| 425 | + data[size++] = value; |
| 426 | + } |
| 427 | + |
| 428 | + private: |
| 429 | + // Simple max function (avoiding <algorithm>) |
| 430 | + static size_t max_size_t(size_t a, size_t b) |
| 431 | + { |
| 432 | + return a > b ? a : b; |
| 433 | + } |
| 434 | + |
| 435 | + void ensure_capacity() |
| 436 | + { |
| 437 | + if (size >= committed_elements) |
| 438 | + { |
| 439 | + size_t grow = max_size_t(MinCommit, committed_elements / 2); |
| 440 | + commit_more(committed_elements + grow); |
| 441 | + } |
| 442 | + |
| 443 | + if (size >= reserved_elements) |
| 444 | + { |
| 445 | + grow_reserved(); |
| 446 | + } |
| 447 | + } |
| 448 | + |
| 449 | + void reserve_and_commit(size_t reserve_elems, size_t commit_elems) |
| 450 | + { |
| 451 | + size_t reserve_bytes = reserve_elems * sizeof(void*); |
| 452 | + void** new_block = (void**)VirtualAlloc( |
| 453 | + nullptr, reserve_bytes, MEM_RESERVE, PAGE_READWRITE); |
| 454 | + if (!new_block) |
| 455 | + snmalloc::PALWindows::error("VirtualAlloc failed"); |
| 456 | + |
| 457 | + size_t commit_bytes = commit_elems * sizeof(void*); |
| 458 | + if (!VirtualAlloc(new_block, commit_bytes, MEM_COMMIT, PAGE_READWRITE)) |
| 459 | + { |
| 460 | + VirtualFree(new_block, 0, MEM_RELEASE); |
| 461 | + snmalloc::PALWindows::error("VirtualAlloc failed"); |
| 462 | + } |
| 463 | + |
| 464 | + data = new_block; |
| 465 | + reserved_elements = reserve_elems; |
| 466 | + committed_elements = commit_elems; |
| 467 | + } |
| 468 | + |
| 469 | + void commit_more(size_t new_commit_elements) |
| 470 | + { |
| 471 | + if (new_commit_elements > reserved_elements) |
| 472 | + { |
| 473 | + grow_reserved(); |
| 474 | + return; |
| 475 | + } |
| 476 | + |
| 477 | + size_t old_bytes = committed_elements * sizeof(void*); |
| 478 | + size_t new_bytes = new_commit_elements * sizeof(void*); |
| 479 | + size_t commit_bytes = new_bytes - old_bytes; |
| 480 | + |
| 481 | + if (commit_bytes > 0) |
| 482 | + { |
| 483 | + void* result = VirtualAlloc( |
| 484 | + (char*)data + old_bytes, commit_bytes, MEM_COMMIT, PAGE_READWRITE); |
| 485 | + if (!result) |
| 486 | + error("VirtualAlloc failed"); |
| 487 | + |
| 488 | + committed_elements = new_commit_elements; |
| 489 | + } |
| 490 | + } |
| 491 | + |
| 492 | + void grow_reserved() |
| 493 | + { |
| 494 | + size_t new_reserved = |
| 495 | + reserved_elements == 0 ? MinReserve : reserved_elements * 2; |
| 496 | + size_t new_commit = max_size_t(committed_elements, size + MinCommit); |
| 497 | + |
| 498 | + void** new_block = (void**)VirtualAlloc( |
| 499 | + nullptr, new_reserved * sizeof(void*), MEM_RESERVE, PAGE_READWRITE); |
| 500 | + if (!new_block) |
| 501 | + error("VirtualAlloc failed"); |
| 502 | + |
| 503 | + if (!VirtualAlloc( |
| 504 | + new_block, new_commit * sizeof(void*), MEM_COMMIT, PAGE_READWRITE)) |
| 505 | + { |
| 506 | + VirtualFree(new_block, 0, MEM_RELEASE); |
| 507 | + error("VirtualAlloc failed"); |
| 508 | + } |
| 509 | + |
| 510 | + // Copy existing values |
| 511 | + for (size_t i = 0; i < size; ++i) |
| 512 | + { |
| 513 | + new_block[i] = data[i]; |
| 514 | + } |
| 515 | + |
| 516 | + VirtualFree(data, 0, MEM_RELEASE); |
| 517 | + data = new_block; |
| 518 | + reserved_elements = new_reserved; |
| 519 | + committed_elements = new_commit; |
| 520 | + } |
| 521 | + |
| 522 | + public: |
| 523 | + void*& operator[](size_t index) |
| 524 | + { |
| 525 | + return data[index]; |
| 526 | + } |
| 527 | + |
| 528 | + const void* operator[](size_t index) const |
| 529 | + { |
| 530 | + return data[index]; |
| 531 | + } |
| 532 | + |
| 533 | + size_t get_size() const |
| 534 | + { |
| 535 | + return size; |
| 536 | + } |
| 537 | + |
| 538 | + size_t get_capacity() const |
| 539 | + { |
| 540 | + return committed_elements; |
| 541 | + } |
| 542 | + |
| 543 | + void** begin() |
| 544 | + { |
| 545 | + return data; |
| 546 | + } |
| 547 | + |
| 548 | + void** end() |
| 549 | + { |
| 550 | + return data + size; |
| 551 | + } |
| 552 | + |
| 553 | + const void* const* begin() const |
| 554 | + { |
| 555 | + return data; |
| 556 | + } |
| 557 | + |
| 558 | + const void* const* end() const |
| 559 | + { |
| 560 | + return data + size; |
| 561 | + } |
| 562 | + }; |
| 563 | + |
| 564 | + /** |
| 565 | + * This will be destroyed last of all of the |
| 566 | + * statics and globals due to init_seg |
| 567 | + */ |
| 568 | + static inline VirtualVector reservations; |
| 569 | + |
| 570 | +# ifdef PLATFORM_HAS_VIRTUALALLOC2 |
| 571 | + template<bool state_using> |
| 572 | + void* PALWindows::reserve_aligned(size_t size) noexcept |
| 573 | + { |
| 574 | + SNMALLOC_ASSERT(bits::is_pow2(size)); |
| 575 | + SNMALLOC_ASSERT(size >= minimum_alloc_size); |
| 576 | + |
| 577 | + DWORD flags = MEM_RESERVE; |
| 578 | + |
| 579 | + if (state_using) |
| 580 | + flags |= MEM_COMMIT; |
| 581 | + |
| 582 | + // If we're on Windows 10 or newer, we can use the VirtualAlloc2 |
| 583 | + // function. The FromApp variant is useable by UWP applications and |
| 584 | + // cannot allocate executable memory. |
| 585 | + MEM_ADDRESS_REQUIREMENTS addressReqs = {NULL, NULL, size}; |
| 586 | + |
| 587 | + MEM_EXTENDED_PARAMETER param = { |
| 588 | + {MemExtendedParameterAddressRequirements, 0}, {0}}; |
| 589 | + // Separate assignment as MSVC doesn't support .Pointer in the |
| 590 | + // initialisation list. |
| 591 | + param.Pointer = &addressReqs; |
| 592 | + |
| 593 | + void* ret = VirtualAlloc2FromApp( |
| 594 | + nullptr, nullptr, size, flags, PAGE_READWRITE, ¶m, 1); |
| 595 | + if (ret == nullptr) |
| 596 | + errno = ENOMEM; |
| 597 | + |
| 598 | + reservations.push_back(ret); |
| 599 | + |
| 600 | + return ret; |
| 601 | + } |
| 602 | +# endif |
| 603 | + |
| 604 | + void* PALWindows::reserve(size_t size) noexcept |
| 605 | + { |
| 606 | + void* ret = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE); |
| 607 | + if (ret == nullptr) |
| 608 | + errno = ENOMEM; |
| 609 | + |
| 610 | + reservations.push_back(ret); |
| 611 | + |
| 612 | + return ret; |
| 613 | + } |
| 614 | + |
373 | 615 | } |
374 | 616 | #endif |
0 commit comments