1 module des.util.arch.emm;
2 
3 import des.util.arch.tree;
4 
5 import des.util.testsuite;
6 
7 ///
8 interface ExternalMemoryManager : TNode!(ExternalMemoryManager,"","EMM")
9 {
10     protected
11     {
12         ///
13         @property void isDestroyed( bool d );
14 
15         ///
16         void selfConstruct();
17         ///
18         void selfDestroy();
19         ///
20         void preChildsDestroy();
21     }
22 
23     ///
24     @property bool isDestroyed() const;
25 
26     final
27     {
28         ///
29         T registerChildEMM(T)( T obj, bool if_orphan=false )
30             if( is( T == class ) || is( T == interface ) )
31         {
32             if( auto cemm = cast(ExternalMemoryManager)obj )
33                 if( ( if_orphan && cemm.parentEMM is null ) || !if_orphan )
34                     attachChildsEMM( cemm );
35             return obj;
36         }
37 
38         ///
39         T[] registerChildEMM(T)( T[] objs, bool if_orphan=false )
40             if( is( T == class ) || is( T == interface ) )
41         {
42             foreach( obj; objs )
43                 registerChildEMM( obj, if_orphan );
44             return objs;
45         }
46 
47         ///
48         T newEMM(T,Args...)( Args args )
49         { return registerChildEMM( new T(args) ); }
50 
51         ///
52         void destroy()
53         {
54             if( isDestroyed ) return;
55             preChildsDestroy();
56             foreach( cemm; childsEMM )
57                 cemm.destroy();
58             selfDestroy();
59             isDestroyed = true;
60         }
61     }
62 
63     ///
64     mixin template EMM(string file=__FILE__,size_t line=__LINE__)
65     {
66         static if( !is(typeof(__EMM_BASE_IMPLEMENT)) )
67         {
68             protected enum __EMM_BASE_IMPLEMENT = true;
69 
70             mixin TNodeHelperEMM!(true,true);
71 
72             private bool is_destroyed = false;
73             public final bool isDestroyed() const { return is_destroyed; }
74             protected final void isDestroyed( bool d )
75             {
76                 bool change = is_destroyed != d;
77                 is_destroyed = d;
78                 if( change && !is_destroyed )
79                     selfConstruct();
80             }
81 
82             import std.traits;
83 
84             protected override
85             {
86                 static if( isAbstractFunction!selfConstruct ) void selfConstruct() {}
87                 static if( isAbstractFunction!selfDestroy ) void selfDestroy() {}
88                 static if( isAbstractFunction!preChildsDestroy ) void preChildsDestroy() {}
89             }
90         }
91         else
92         {
93             version(emmcheck)
94                 pragma(msg, format( "WARNING: duplicate mixin EMM at %s:%d", file, line ) );
95         }
96     }
97 }
98 
99 unittest
100 {
101     string[] log;
102 
103     class Test : ExternalMemoryManager
104     {
105         mixin EMM;
106         string name;
107         this( string name ) { this.name = name; }
108     protected:
109         void preChildsDestroy()
110         { log ~= name ~ ".preChildsDestroy"; }
111         void selfDestroy()
112         { log ~= name ~ ".selfDestroy"; }
113     }
114 
115     auto a = new Test( "a" );
116     auto b = new Test( "b" );
117     auto c = new Test( "c" );
118     auto d = new Test( "d" );
119 
120     assertNull( a.parentEMM );
121     assertNull( b.parentEMM );
122     assertNull( c.parentEMM );
123     assertNull( d.parentEMM );
124 
125     assertNull( a.childsEMM );
126     assertNull( b.childsEMM );
127     assertNull( c.childsEMM );
128     assertNull( d.childsEMM );
129 
130     a.registerChildEMM( b );
131     assertEq( a.childsEMM.length, 1 );
132     assertEq( a.childsEMM[0], b );
133     assertEq( b.parentEMM, a );
134 
135     c.registerChildEMM( b );
136     assertEq( a.childsEMM.length, 0 );
137     assertEq( c.childsEMM.length, 1 );
138     assertEq( c.childsEMM[0], b );
139     assertEq( b.parentEMM, c );
140 
141     a.registerChildEMM( b, true );
142     assertEq( a.childsEMM.length, 0 );
143     assertEq( c.childsEMM.length, 1 );
144     assertEq( c.childsEMM[0], b );
145     assertEq( b.parentEMM, c );
146 
147     a.registerChildEMM( b );
148     assertEq( a.childsEMM.length, 1 );
149     assertEq( a.childsEMM[0], b );
150     assertEq( b.parentEMM, a );
151     assertEq( c.childsEMM.length, 0 );
152 
153     mustExcept({ b.registerChildEMM( a ); });
154 
155     a.registerChildEMM( d );
156     assertEq( a.childsEMM.length, 2 );
157     assertEq( a.childsEMM[1], d );
158 
159     d.destroy();
160     assertEq( a.childsEMM.length, 2 );
161     assertEq( log, [ "d.preChildsDestroy", "d.selfDestroy" ] );
162 
163     assertNull( c.parentEMM );
164     assertNull( c.childsEMM );
165 
166     a.destroy();
167     assertEq( log, [ "d.preChildsDestroy",
168                      "d.selfDestroy",
169                      "a.preChildsDestroy",
170                      "b.preChildsDestroy",
171                      "b.selfDestroy",
172                      "a.selfDestroy"
173                    ] );
174 }