1 /**
2  * Small utility library to ensure a reference is emplaed on the stack.
3  *
4  * The empty asm call is currently enough to force all current compilers to
5  * give up on trying to optimize the destructor out. If this becomes incorrect
6  * at some point, then a revisit is necessary.
7  *
8  * License: Boost-1.0. See LICENSE.MD
9  *
10  * Authors: Steven Schveighoffer
11  *
12  * Copyright: 2021 Steven Schveighoffer
13  */
14 module keepalive;
15 
16 /**
17  * Create a guaranteed-stack-allocated reference. While the thing itself cannot
18  * be copied, it should be usable just like the reference that it wraps.
19  * Because of the destructor, it is guaranteed to be put on the stack.
20  *
21  * Params:
22  *      val: The value to wrap. It must be a reference type.
23  * Returns:
24  *      A voldemort type that wraps the value. Using it should be no different
25  *      than the original pointer, 
26  */
27 @nogc nothrow @safe pure
28 auto keepAlive(T)(T val) if (is(T == U*, U) || is(T == class) || is(T == interface))
29 {
30     static struct KeepAlive
31     {
32         T _val;
33         alias _val this;
34         @disable this(this);
35 
36         @nogc nothrow @safe pure ~this() {
37             version (GNU) asm @nogc nothrow @safe pure { "" :: "rm" (this._val); }
38             else asm @nogc nothrow @safe pure {}
39         }
40     }
41     return KeepAlive(val);
42 }
43 
44 /// Note, it's not considered a hard failure for the first test to fail, as
45 /// it's within the compiler's right to place any pointer it wants onto the
46 /// stack. In the case that does happen, print a warning.
47 unittest
48 {
49     static class C {
50         static int dtorcalls = 0;
51         ~this() { ++dtorcalls; }
52     }
53 
54     import core.memory;
55 
56     auto c = new C;
57     foreach(i; 0 .. 100) GC.collect;
58     if(C.dtorcalls != 1)
59     {
60         import std.stdio;
61         writeln("WARNING: keepalive issue not detected");
62     }
63     C.dtorcalls = 0;
64 
65     auto c2 = keepAlive(new C);
66     foreach(i; 0 .. 100) GC.collect;
67     assert(C.dtorcalls == 0);
68 }