|
32 | 32 |
|
33 | 33 | #ifdef MS_WINDOWS
|
34 | 34 | #include <windows.h>
|
| 35 | +#include <winternl.h> |
35 | 36 | static int
|
36 | 37 | my_getpagesize(void)
|
37 | 38 | {
|
@@ -376,14 +377,15 @@ is_resizeable(mmap_object *self)
|
376 | 377 | {
|
377 | 378 | if (self->exports > 0) {
|
378 | 379 | PyErr_SetString(PyExc_BufferError,
|
379 |
| - "mmap can't resize with extant buffers exported."); |
| 380 | + "mmap can't resize with extant buffers exported."); |
380 | 381 | return 0;
|
381 | 382 | }
|
382 | 383 | if ((self->access == ACCESS_WRITE) || (self->access == ACCESS_DEFAULT))
|
383 | 384 | return 1;
|
384 | 385 | PyErr_Format(PyExc_TypeError,
|
385 |
| - "mmap can't resize a readonly or copy-on-write memory map."); |
| 386 | + "mmap can't resize a readonly or copy-on-write memory map."); |
386 | 387 | return 0;
|
| 388 | + |
387 | 389 | }
|
388 | 390 |
|
389 | 391 |
|
@@ -503,51 +505,110 @@ mmap_resize_method(mmap_object *self,
|
503 | 505 | }
|
504 | 506 |
|
505 | 507 | {
|
| 508 | + /* |
| 509 | + To resize an mmap on Windows: |
| 510 | +
|
| 511 | + - Close the existing mapping |
| 512 | + - If the mapping is backed to a named file: |
| 513 | + unmap the view, clear the data, and resize the file |
| 514 | + If the file can't be resized (eg because it has other mapped references |
| 515 | + to it) then let the mapping be recreated at the original size and set |
| 516 | + an error code so an exception will be raised. |
| 517 | + - Create a new mapping of the relevant size to the same file |
| 518 | + - Map a new view of the resized file |
| 519 | + - If the mapping is backed by the pagefile: |
| 520 | + copy any previous data into the new mapped area |
| 521 | + unmap the original view which will release the memory |
| 522 | + */ |
506 | 523 | #ifdef MS_WINDOWS
|
507 |
| - DWORD dwErrCode = 0; |
508 |
| - DWORD off_hi, off_lo, newSizeLow, newSizeHigh; |
509 |
| - /* First, unmap the file view */ |
510 |
| - UnmapViewOfFile(self->data); |
511 |
| - self->data = NULL; |
512 |
| - /* Close the mapping object */ |
| 524 | + DWORD error = 0, file_resize_error = 0; |
| 525 | + char* old_data = self->data; |
| 526 | + LARGE_INTEGER offset, max_size; |
| 527 | + offset.QuadPart = self->offset; |
| 528 | + max_size.QuadPart = self->offset + new_size; |
| 529 | + /* close the file mapping */ |
513 | 530 | CloseHandle(self->map_handle);
|
514 |
| - self->map_handle = NULL; |
515 |
| - /* Move to the desired EOF position */ |
516 |
| - newSizeHigh = (DWORD)((self->offset + new_size) >> 32); |
517 |
| - newSizeLow = (DWORD)((self->offset + new_size) & 0xFFFFFFFF); |
518 |
| - off_hi = (DWORD)(self->offset >> 32); |
519 |
| - off_lo = (DWORD)(self->offset & 0xFFFFFFFF); |
520 |
| - SetFilePointer(self->file_handle, |
521 |
| - newSizeLow, &newSizeHigh, FILE_BEGIN); |
522 |
| - /* Change the size of the file */ |
523 |
| - SetEndOfFile(self->file_handle); |
524 |
| - /* Create another mapping object and remap the file view */ |
| 531 | + /* if the file mapping still exists, it cannot be resized. */ |
| 532 | + if (self->tagname) { |
| 533 | + self->map_handle = OpenFileMapping(FILE_MAP_WRITE, FALSE, |
| 534 | + self->tagname); |
| 535 | + if (self->map_handle) { |
| 536 | + PyErr_SetFromWindowsErr(ERROR_USER_MAPPED_FILE); |
| 537 | + return NULL; |
| 538 | + } |
| 539 | + } else { |
| 540 | + self->map_handle = NULL; |
| 541 | + } |
| 542 | + |
| 543 | + /* if it's not the paging file, unmap the view and resize the file */ |
| 544 | + if (self->file_handle != INVALID_HANDLE_VALUE) { |
| 545 | + if (!UnmapViewOfFile(self->data)) { |
| 546 | + return PyErr_SetFromWindowsErr(GetLastError()); |
| 547 | + }; |
| 548 | + self->data = NULL; |
| 549 | + /* resize the file */ |
| 550 | + if (!SetFilePointerEx(self->file_handle, max_size, NULL, |
| 551 | + FILE_BEGIN) || |
| 552 | + !SetEndOfFile(self->file_handle)) { |
| 553 | + /* resizing failed. try to remap the file */ |
| 554 | + file_resize_error = GetLastError(); |
| 555 | + new_size = max_size.QuadPart = self->size; |
| 556 | + } |
| 557 | + } |
| 558 | + |
| 559 | + /* create a new file mapping and map a new view */ |
| 560 | + /* FIXME: call CreateFileMappingW with wchar_t tagname */ |
525 | 561 | self->map_handle = CreateFileMapping(
|
526 | 562 | self->file_handle,
|
527 | 563 | NULL,
|
528 | 564 | PAGE_READWRITE,
|
529 |
| - 0, |
530 |
| - 0, |
| 565 | + max_size.HighPart, |
| 566 | + max_size.LowPart, |
531 | 567 | self->tagname);
|
532 |
| - if (self->map_handle != NULL) { |
533 |
| - self->data = (char *) MapViewOfFile(self->map_handle, |
534 |
| - FILE_MAP_WRITE, |
535 |
| - off_hi, |
536 |
| - off_lo, |
537 |
| - new_size); |
| 568 | + |
| 569 | + error = GetLastError(); |
| 570 | + if (error == ERROR_ALREADY_EXISTS) { |
| 571 | + CloseHandle(self->map_handle); |
| 572 | + self->map_handle = NULL; |
| 573 | + } |
| 574 | + else if (self->map_handle != NULL) { |
| 575 | + self->data = MapViewOfFile(self->map_handle, |
| 576 | + FILE_MAP_WRITE, |
| 577 | + offset.HighPart, |
| 578 | + offset.LowPart, |
| 579 | + new_size); |
538 | 580 | if (self->data != NULL) {
|
| 581 | + /* copy the old view if using the paging file */ |
| 582 | + if (self->file_handle == INVALID_HANDLE_VALUE) { |
| 583 | + memcpy(self->data, old_data, |
| 584 | + self->size < new_size ? self->size : new_size); |
| 585 | + if (!UnmapViewOfFile(old_data)) { |
| 586 | + error = GetLastError(); |
| 587 | + } |
| 588 | + } |
539 | 589 | self->size = new_size;
|
540 |
| - Py_RETURN_NONE; |
541 |
| - } else { |
542 |
| - dwErrCode = GetLastError(); |
| 590 | + } |
| 591 | + else { |
| 592 | + error = GetLastError(); |
543 | 593 | CloseHandle(self->map_handle);
|
544 | 594 | self->map_handle = NULL;
|
545 | 595 | }
|
546 |
| - } else { |
547 |
| - dwErrCode = GetLastError(); |
548 | 596 | }
|
549 |
| - PyErr_SetFromWindowsErr(dwErrCode); |
550 |
| - return NULL; |
| 597 | + |
| 598 | + if (error) { |
| 599 | + return PyErr_SetFromWindowsErr(error); |
| 600 | + return NULL; |
| 601 | + } |
| 602 | + /* It's possible for a resize to fail, typically because another mapping |
| 603 | + is still held against the same underlying file. Even if nothing has |
| 604 | + failed -- ie we're still returning a valid file mapping -- raise the |
| 605 | + error as an exception as the resize won't have happened |
| 606 | + */ |
| 607 | + if (file_resize_error) { |
| 608 | + PyErr_SetFromWindowsErr(file_resize_error); |
| 609 | + return NULL; |
| 610 | + } |
| 611 | + Py_RETURN_NONE; |
551 | 612 | #endif /* MS_WINDOWS */
|
552 | 613 |
|
553 | 614 | #ifdef UNIX
|
|
0 commit comments