[PATCH v3 0/1] MR11742: krnl386: Don't free DISCARDABLE resources in FreeResource16.
If a resource is marked DISCARDABLE, it is not actually freed when its refcount reaches 0. In theory the resource's memory is freed when the system is low on memory, but its global handle remains valid to allow the resource to be resurrected with the same handle later. In practice, 16-bit programs can't run out of memory on modern hardware, and the game Tetris Jr. depends on its discardable resources never being discarded. See https://devblogs.microsoft.com/oldnewthing/20040202-00/?p=40783 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=23364 -- v3: krnl386: Don't free DISCARDABLE resources in FreeResource16. https://gitlab.winehq.org/wine/wine/-/merge_requests/11742
From: Alex Henrie <alexhenrie24@gmail.com> If a resource is marked DISCARDABLE, it is not actually freed when its refcount reaches 0. In theory the resource's memory is freed when the system is low on memory, but its global handle remains valid to allow the resource to be resurrected with the same handle later. In practice, 16-bit programs can't run out of memory on modern hardware, and the game Tetris Jr. depends on its discardable resources never being discarded. See https://devblogs.microsoft.com/oldnewthing/20040202-00/?p=40783 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=23364 --- dlls/krnl386.exe16/resource.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/dlls/krnl386.exe16/resource.c b/dlls/krnl386.exe16/resource.c index bcdb01a3b30..e833297ab4c 100644 --- a/dlls/krnl386.exe16/resource.c +++ b/dlls/krnl386.exe16/resource.c @@ -1023,6 +1023,8 @@ HGLOBAL16 WINAPI LoadResource16( HMODULE16 hModule, HRSRC16 hRsrc ) if (pNameInfo->handle && !(GlobalFlags16(pNameInfo->handle) & GMEM_DISCARDED)) { pNameInfo->usage++; + GlobalReAlloc16( pNameInfo->handle, 0, + (GlobalFlags16( pNameInfo->handle ) & ~GMEM_DISCARDABLE) | GMEM_MODIFY ); TRACE(" Already loaded, new count=%d\n", pNameInfo->usage ); } else @@ -1127,9 +1129,17 @@ BOOL16 WINAPI FreeResource16( HGLOBAL16 handle ) if (pNameInfo->usage > 0) pNameInfo->usage--; if (pNameInfo->usage == 0) { - GlobalFree16( pNameInfo->handle ); - pNameInfo->handle = 0; - pNameInfo->flags &= ~NE_SEGFLAGS_LOADED; + if (pNameInfo->flags & NE_SEGFLAGS_DISCARDABLE) + { + GlobalReAlloc16( pNameInfo->handle, 0, + GlobalFlags16( pNameInfo->handle ) | GMEM_DISCARDABLE | GMEM_MODIFY ); + } + else + { + GlobalFree16( pNameInfo->handle ); + pNameInfo->handle = 0; + pNameInfo->flags &= ~NE_SEGFLAGS_LOADED; + } } return FALSE; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11742
On Mon Aug 24 14:55:17 2026 +0000, Jinoh Kang wrote:
16-bit programs are limited to 4 GiB of address space. They will run out of address space before they can use up all of the computer's memory. I meant that the DISCARDABLE mechanism (if ever implemented) is needed exactly for that: when the application runs out of (segmented) address space and no new segments can be allocated. The segment has to remain allocated to the resource even if it is discarded, so that the resource can be reloaded later and still have the same handle as before. This commit does not use up any more segments than Windows does.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/11742#note_149689
On Mon Aug 24 15:10:43 2026 +0000, Alex Henrie wrote:
changed this line in [version 3 of the diff](/wine/wine/-/merge_requests/11742/diffs?diff_id=292949&start_sha=071a7be2a170f911a5c00939471e679b815f30db#8f8b226b0534fb7a7fb2f1c8d47c5e0d57902d2d_1135_1134) Done
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/11742#note_149690
This merge request was approved by Jinoh Kang. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11742
On Mon Aug 24 15:13:02 2026 +0000, Alex Henrie wrote:
The segment has to remain allocated to the resource even if it is discarded, so that the resource can be reloaded later and still have the same handle as before. This commit does not use up any more segments than Windows does. In other words, "discardable" means that the segment's memory can be discarded, but not the segment selector itself. The "discardable" mechanism is only for reclaiming memory; it can't reclaim segment selectors.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/11742#note_149695
On Mon Aug 24 15:50:01 2026 +0000, Alex Henrie wrote:
Done Thanks for the feedback!
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/11742#note_149696
On Mon Aug 24 16:13:26 2026 +0000, Alex Henrie wrote:
In other words, "discardable" means that the segment's memory can be discarded, but not the segment selector itself. The "discardable" mechanism is only for reclaiming memory; it can't reclaim segment selectors. Sorry, my mind was stuck in win16 standard mode.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/11742#note_149699
On Mon Aug 24 19:27:03 2026 +0000, Jinoh Kang wrote:
Sorry, my mind was stuck in win16 standard mode. No problem, it is confusing. Thinking about it some more, my explanation wasn't perfectly accurate. If the system is running low on memory, memory allocations might start failing, and Wine could use that as a signal to discard discardable segments and retry the memory allocation. Still, it's unlikely that a 16-bit program would have enough discardable memory to alleviate such an extreme situation. And in practice, the OOM killer is going to kill something before any allocations start to fail.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/11742#note_149736
participants (3)
-
Alex Henrie -
Alex Henrie (@alexhenrie) -
Jinoh Kang (@iamahuman)