1 /**
2  * Memory and GC stuff.
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <ae@cy.md>
12  */
13 
14 module ae.sys.memory;
15 
16 import core.exception;
17 import core.memory;
18 import core.thread;
19 
20 /// Did the GC run since this function's last call on this thread?
21 /// Not 100% reliable (due to false pointers).
22 bool gcRan()
23 {
24 	static bool initialized = false;
25 	static bool destroyed = false;
26 
27 	static class Beacon
28 	{
29 		~this() @nogc
30 		{
31 			destroyed = true;
32 		}
33 	}
34 
35 	if (!initialized)
36 	{
37 		destroyed = false;
38 		new Beacon();
39 		initialized = true;
40 	}
41 
42 	bool result = destroyed;
43 	if (destroyed)
44 	{
45 		destroyed = false;
46 		new Beacon();
47 	}
48 
49 	return result;
50 }
51 
52 /// Is the given pointer located on the stack of the current thread?
53 /// Useful to assert on before taking the address of e.g. a struct member.
54 bool onStack(const(void)* p)
55 {
56 	auto p0 = thread_stackTop();
57 	auto p1 = thread_stackBottom();
58 	return p0 <= p && p <= p1;
59 }
60 
61 unittest
62 {
63 	/* .......... */ int l; auto pl = &l;
64 	static /* ... */ int s; auto ps = &s;
65 	static __gshared int g; auto pg = &g;
66 	/* ................. */ auto ph = new int;
67 	assert( pl.onStack());
68 	assert(!ps.onStack());
69 	assert(!pg.onStack());
70 	assert(!ph.onStack());
71 }
72 
73 private void callExtend()
74 {
75 	// Call GC.extend with dummy data.
76 	// It will normally exit silently if given a null pointer,
77 	// but not before the reentrance check.
78 	GC.extend(null, 0, 0, null);
79 }
80 
81 /// Asserts that we are not inside a GC collection cycle,
82 /// by performing a no-op GC operation.
83 /// If we are, an `InvalidMemoryOperationError` is raised by the runtime.
84 void assertNotInCollect() @nogc
85 {
86 	(cast(void function() @nogc)&callExtend)();
87 }
88 
89 /// Checks if we are inside a GC collection cycle.
90 /// This is currently done in a dumb and expensive way, so use sparingly.
91 bool inCollect() @nogc
92 {
93 	try
94 		assertNotInCollect();
95 	catch (InvalidMemoryOperationError)
96 		return true;
97 	return false;
98 }
99 
100 unittest
101 {
102 	assert(!inCollect());
103 
104 	class C
105 	{
106 		static bool tested;
107 
108 		~this() @nogc
109 		{
110 			assert(inCollect());
111 			tested = true;
112 		}
113 	}
114 
115 	foreach (n; 0..128)
116 		new C;
117 	GC.collect();
118 	assert(C.tested);
119 }