Bug 21097 – [REG2.083] Stack exhaustion upon large struct .destroy

Status
RESOLVED
Resolution
FIXED
Severity
regression
Priority
P1
Component
druntime
Product
D
Version
D2
Platform
All
OS
All
Creation time
2020-07-30T23:36:45Z
Last change time
2021-05-28T11:58:11Z
Keywords
industry, pull
Assigned to
No Owner
Creator
johanengelen

Comments

Comment #0 by johanengelen — 2020-07-30T23:36:45Z
Testcase: ``` import std.stdio; struct S { // doesn't have to be this large to exhaust the stack of course ubyte[10_000_000] i = [1,2]; void close() { destroy(this); } } void main() { auto s = new S(); s.close(); } ``` The problem is that the instantiated druntime template to write S.init to the object uses a stack allocated temporary with the same size as the object --> stack exhaustion. Prior versions used a global statically allocated variable and thus did not have the problem. (but there was duplication of data) There's been a lot of work on the object.destroy code for structs, I can't trace back history easily. What works: LDC 1.13.0 (dlang 2.083.1) What doesn't: LDC 1.14 (dlang 2.084)
Comment #1 by johanengelen — 2020-07-30T23:59:42Z
Note: the 10MB symbol may lead one to believe that this is only a problem in rare cases. It isn't. At Weka, this bug is triggered with a dynamically allocated 300kb struct. Fibers do not have very large stacks.
Comment #2 by johanengelen — 2020-07-31T09:54:19Z
Two potential solutions: ``` // Avoid stack allocation, at the cost of virtual call to get the init symbol. { auto arr = cast(ubyte[])typeid(T).initializer(); if (arr.ptr is null) { (cast(ubyte*)val)[0 .. T.sizeof] = ubyte(0); } else { // Use fill to duplicate 'arr' to work around https://issues.dlang.org/show_bug.cgi?id=16394 (cast(ubyte*)val)[0 .. T.sizeof].fillbytes(arr); } } // Avoid stack allocation, at the cost of duplicating the init symbol (binary size increase) { import core.stdc.string : memcpy; shared static immutable T init = T.init; memcpy(&chunk, &init, T.sizeof); } ``` (one cannot access the init symbol directly)
Comment #3 by dlang-bot — 2021-05-10T21:07:28Z
@JohanEngelen created dlang/druntime pull request #3467 "Fix Issue 21097 - prevent stack allocation when destroying large aggregates" fixing this issue: - Fix Issue 21097 - Fix destroy and core.internal.lifetime.emplaceInitializer for large aggregates by preventing stack allocation https://github.com/dlang/druntime/pull/3467
Comment #4 by dlang-bot — 2021-05-10T21:13:35Z
@JohanEngelen created dlang/dmd pull request #12509 "Add tests for the druntime fix for destroy on large aggregates (issue 21097)" mentioning this issue: - Add tests for the druntime fix for destroy on large aggregates (issue 21097) https://github.com/dlang/dmd/pull/12509
Comment #5 by dlang-bot — 2021-05-18T23:41:51Z
dlang/druntime pull request #3467 "Fix Issue 21097 - prevent stack allocation when destroying large aggregates" was merged into stable: - 5981d49df518185725328538ab7b7b13879520e7 by Johan Engelen: Fix Issue 21097 - Fix destroy and core.internal.lifetime.emplaceInitializer for large aggregates by preventing stack allocation https://github.com/dlang/druntime/pull/3467
Comment #6 by dlang-bot — 2021-05-28T11:58:11Z
dlang/druntime pull request #3482 "merge stable" was merged into master: - 706815547a6d7219dc39ef2f392c6139b6e943db by Johan Engelen: Fix Issue 21097 - Fix destroy and core.internal.lifetime.emplaceInitializer for large aggregates by preventing stack allocation https://github.com/dlang/druntime/pull/3482