001    // MemoryArea.java, created by wbeebee
002    // Copyright (C) 2001 Wes Beebee <wbeebee@mit.edu>
003    // Licensed under the terms of the GNU GPL; see COPYING for details.
004    package javax.realtime;
005    
006    import java.lang.reflect.Array;
007    import java.lang.reflect.Constructor;
008    import java.lang.reflect.InvocationTargetException;
009    
010    /**
011     * @author Wes Beebee <<a href="mailto:wbeebee@mit.edu">wbeebee@mit.edu</a>>
012     */
013    
014    /** <code>MemoryArea</code> is the abstract base class of all classes dealing
015     *  with the representations of allocatable memory areas, including the
016     *  immortal memory area, physical memory and scoped memory areas.
017     */
018    public abstract class MemoryArea {
019    
020        /** The size of this MemoryArea. */
021        protected long size;
022    
023        /** The size of the consumed memory */
024        // Size is somewhat inaccurate
025        protected long memoryConsumed;
026    
027        /** Indicates whether this is a ScopedMemory. */
028        boolean scoped;
029    
030        /** Indicates whether this is a HeapMemory. */
031        boolean heap;
032    
033        /** Indicates whether this is a NullMemoryArea. */
034        protected boolean nullMem;
035    
036        /** Every MemoryArea has a unique ID. */
037        protected int id;
038    
039        /** The run() method of this object is called whenever enter() is called. */
040        protected Runnable logic;
041    
042        /** This is used to create the unique ID. */
043        private static int num = 0;
044    
045        /** Indicates whether this memoryArea refers to a constant or not. 
046         *  This is set by the compiler.
047         */
048        boolean constant;
049    
050        /** The shadow of <code>this</code>. */
051        MemoryArea shadow;
052      
053        abstract protected void initNative(long sizeInBytes);
054    
055        /** Create an instance of <code>MemoryArea</code>.
056         *
057         *  @param sizeInBytes The size of <code>MemoryArea</code> to allocate, in bytes.
058         */
059        protected MemoryArea(long sizeInBytes) {
060            size = sizeInBytes;
061            preSetup();
062            initNative(sizeInBytes);
063            postSetup();
064        }
065    
066        protected void preSetup() {
067            scoped = false; 
068            heap = false;
069            id = num++;
070            constant = false;
071            nullMem = false;
072        }
073        
074        protected void postSetup() {
075            (shadow = shadow()).shadow = shadow;
076            registerFinal();
077        }
078    
079        // Do we really need this abstract method?
080        // protected abstract void initNative(long sizeInBytes);
081    
082        /** Create an instance of <code>MemoryArea</code>.
083         *
084         *  @param sizeInBytes The size of <code>MemoryArea</code> to allocate, in bytes.
085         *  @param logic The <code>run()</code> method of this object will be called
086         *               whenever <code>enter()</code> is called.
087         */
088        protected MemoryArea(long sizeInBytes, Runnable logic) {
089            this(sizeInBytes);
090            this.logic = logic;
091        }
092    
093        /** Create an instance of <code>MemoryArea</code>.
094         *
095         *  @param size A <code>SizeEstimator</code> object which indicates the amount
096         *              of memory required by this <code>MemoryArea</code>.
097         */
098        protected MemoryArea(SizeEstimator size) {
099            this(size.getEstimate());
100        }
101    
102        /** Create an instance of <code>MemoryArea</code>.
103         *
104         *  @param size A <code>SizeEstimator</code> object which indicates the amount
105         *              of memory required by this <code>MemoryArea</code>.
106         *  @param logic The <code>run()</code> method of this object will be called
107         *               whenever <code>enter()</code> is called.
108         */
109        protected MemoryArea(SizeEstimator size,
110                             Runnable logic) {
111            this(size);
112            this.logic = logic;
113        }
114    
115        /** Associate this memory area to the current real-time thread for the
116         *  duration of the execution of the <code>run()</code> method of the
117         *  <code>java.lang.Runnable</code> passed at construction time. During
118         *  this bound period of execution, all objects are allocated from the
119         *  memory area until another one takes effect, or the <code>enter()</code>
120         *  method is exited. A runtime exception is thrown if this method is
121         *  called from thread other than a <code>RealtimeThread</code> or
122         *  <code>NoHeapRealtimeThrea</code>.
123         *
124         *  @throws java.lang.IllegalArgumentException Thrown if no <code>Runnable</code>
125         *                                             was passed in the constructor.
126         */
127        public void enter() {
128            enter(this.logic);
129        }
130    
131        /** Associate this memory area to the current real-time thread for the
132         *  duration of the execution of the <code>run()</code> method of the
133         *  <code>java.lang.Runnable</code> passed at construction time. During
134         *  this bound period of execution, all objects are allocated from the
135         *  memory area until another one takes effect, or the <code>enter()</code>
136         *  method is exited. A runtime exception is thrown if this method is
137         *  called from thread other than a <code>RealtimeThread</code> or
138         *  <code>NoHeapRealtimeThrea</code>.
139         *
140         *  @param logic The <code>Runnable</code> object whose <code>run()</code>
141         *               method should be invoked.
142         */
143        public void enter(Runnable logic) {
144            RealtimeThread.checkInit();
145            RealtimeThread current = RealtimeThread.currentRealtimeThread();
146            MemoryArea oldMem = current.memoryArea();
147            current.enter(shadow, this);
148            try {
149                logic.run();
150            } catch (Throwable e) {
151                try {
152                    e.memoryArea = getMemoryArea(e);
153                    oldMem.checkAccess(e);
154                } catch (Exception checkException) {
155                    current.exitMem();
156                    throw new ThrowBoundaryError("An exception occurred that was " +
157                                                 "allocated in an inner scope that " +
158                                                 "just exited.");
159                }
160                current.exitMem();
161                throw new ThrowBoundaryError(e.toString());
162            }
163            current.exitMem();
164        }
165    
166        /** Execute the <code>run()</code> method from the <code>logic</code> parameter
167         *  using this memory area as the current allocation context. If the memory
168         *  area is a scoped memory type, this method behaves as if it had moved the
169         *  allocation context up the scope stack to the occurrence of the memory area.
170         *  If the memory area is heap or immortal memory, this method behaves as if
171         *  the <code>run()</code> method were running in that memory type with an
172         *  empty scope stack.
173         *
174         *  @param logic The runnable object whose <code>run()</code> method should be
175         *               executed.
176         *  @throws IllegalStateException A non-realtime thread attempted to enter the
177         *                                memory area.
178         *  @throws InaccessibleAreaException The memory area is not in the thread's
179         *                                    scope stack.
180         */
181        public void executeInArea(Runnable logic)
182            throws InaccessibleAreaException {
183            enter(logic);  // In our system, this is a subset of enter for all practical purposes...
184        }
185    
186        /** Gets the <code>MemoryArea</code> in which the given object is located.
187         *
188         *  @return The current instance of <code>MemoryArea</code> of the object.
189         */
190        public static MemoryArea getMemoryArea(Object object) {
191            if (object == null) {
192                return ImmortalMemory.instance();
193            }
194            MemoryArea mem = object.memoryArea;
195            if (mem == null) { // Native methods return objects 
196                // allocated out of the current scope.
197                return RealtimeThread.currentRealtimeThread().memoryArea();
198            } 
199            if (mem.constant) { 
200                // Constants are allocated out of ImmortalMemory
201                // Also, static objects before RTJ is setup...
202                return ImmortalMemory.instance();
203            }
204            return mem;
205        }
206        
207        /** An exact count, in bytes, of the all of the memory currently used by the
208         *  system for the allocated objects.
209         *
210         *  @return The amount of memory consumed.
211         */
212        public long memoryConsumed() {
213            return memoryConsumed;
214        }
215        
216        /** An approximation to the total amount of memory currently available for
217         *  future allocated objects, measured in bytes.
218         *
219         *  @return The amount of remaining memory in bytes.
220         */
221        public long memoryRemaining() {
222            return size() - memoryConsumed();
223        }
224        
225        protected native Object newArray(RealtimeThread rt, 
226                                         Class type, 
227                                         int number);
228        protected native Object newArray(RealtimeThread rt, 
229                                         Class type, int[] dimensions);
230    
231        /** Allocate an array of the given type in this memory area.
232         *
233         *  @param type The class of the elements of the new array.
234         *  @param number The number of elements in the new array.
235         *  @throws java.lang.IllegalAccessException The class or initializer is
236         *                                           inaccessible.
237         *  @throws java.lang.InstantiationException The array cannot be instantiated.
238         *  @throws OutOfMemoryError Space in the memory area is exhaused.
239         */
240        public Object newArray(final Class type, final int number) 
241            throws IllegalAccessException, InstantiationException,
242                   OutOfMemoryError {
243            RealtimeThread.checkInit();
244            RealtimeThread rt = RealtimeThread.currentRealtimeThread();
245            if (number<0) throw new NegativeArraySizeException();
246    
247            rt.memoryArea().checkNewInstance(shadow);
248            Object o;
249            (o = newArray(rt, type, number)).memoryArea = shadow;
250            return o;
251        }
252    
253        static Class[] nullClassArr = null;
254        static Object[] nullObjArr = null;
255    
256        /** Allocate an object in this memory area.
257         *
258         *  @param type The class of which to create a new instance.
259         *  @throws java.lang.IllegalAccessException The class or initializer is
260         *                                           inaccessible.
261         *  @throws java.lang.InstantiationException The specified class object
262         *                                           could not be instantiated.
263         *                                           Possible causes are: it is an
264         *                                           interface; it is abstract; it
265         *                                           is an array, or an exception
266         *                                           was thrown by the constructor.
267         *  @throws OutOfMemoryError Space in the memory area is exhaused.
268         */
269    
270        public Object newInstance(Class type)
271            throws IllegalAccessException, InstantiationException, OutOfMemoryError {
272            if (nullClassArr == null) {
273                nullClassArr = new Class[0];
274                nullObjArr = new Object[0];
275                nullClassArr.memoryArea = HeapMemory.instance();
276                nullObjArr.memoryArea = HeapMemory.instance();
277            }
278            if (nullClassArr.memoryArea.heap&&(!RealtimeThread.RTJ_init_in_progress)) {
279                final ImmortalMemory im = ImmortalMemory.instance();
280                nullClassArr = (Class[])im.newArray(Class.class, 0);
281                nullObjArr = (Object[])im.newArray(Object.class, 0);
282            }
283            return newInstance(type, nullClassArr, nullObjArr);
284        }
285    
286        /** Allocate an object in this memory area. */
287        public Object newInstance(final Class type,
288                                  final Class[] parameterTypes,
289                                  final Object[] parameters) 
290            throws IllegalAccessException, InstantiationException, OutOfMemoryError {
291            Constructor c;
292            try {
293                // Type.getConstructor doesn't allocate any that needs deallocation 
294                c = type.getConstructor(parameterTypes); 
295            } catch (Exception e) {
296                throw new InstantiationException(e.toString());
297            }
298            
299            return newInstance(c, parameters);
300        }
301        
302        /** Allocate an object in this memory area.
303         *
304         *  @throws java.lang.IllegalAccessException The class or initializer is
305         *                                           inaccessible.
306         *  @throws java.lang.InstantiationException The specified class object
307         *                                           could not be instantianted.
308         *                                           Possible causes are: it is an
309         *                                           interface, it is abstract, it
310         *                                           is an array, or an exception
311         *                                           was thrown by the constructor.
312         *  @throws OutOfMemoryError Space in the memory area is exhaused.
313         */
314        public Object newInstance(Constructor c, Object[] args)
315            throws IllegalAccessException, InstantiationException,
316                   OutOfMemoryError {
317            try {
318                RealtimeThread.checkInit();
319                RealtimeThread rt = RealtimeThread.currentRealtimeThread();
320                rt.memoryArea().checkNewInstance(shadow);
321                Object o;
322                try {
323                    o = newInstance(rt, c, args);
324                } catch (InvocationTargetException e) {
325                    throw new InstantiationException(e.getMessage());
326                }
327                o.memoryArea = shadow;
328                return o;
329            } catch (IllegalAssignmentError e) {
330                throw new IllegalAccessException(e.toString());
331            }
332        }
333    
334        /** Query the size of the memory area. The returned value is the
335         *  current size. Current size may be larger than initial size for
336         *  those areas that are allowed to grow.
337         */
338        public long size() {
339            return size;
340        }
341        
342    
343        // METHODS NOT IN SPECS
344    
345        protected native Object newInstance(RealtimeThread rt, 
346                                            Constructor constructor, 
347                                            Object[] parameters)
348            throws InvocationTargetException;
349    
350    
351        /** Explicitly unsafe way to get by without polluting the previous scope with a Runnable */
352        static void startMem(MemoryArea mem) {
353            RealtimeThread rt = RealtimeThread.currentRealtimeThread();
354            rt.checkDepth++;
355            rt.enter(mem.shadow, mem);
356        }
357    
358        static void stopMem() {
359            RealtimeThread rt = RealtimeThread.currentRealtimeThread();
360            if ((--rt.checkDepth)<0) {
361                NoHeapRealtimeThread.print("Error: stopMem>startMem\n");
362                System.exit(-1);
363            }
364            rt.exitMem();
365        }
366    
367    
368        protected MemoryArea(long minimum, long maximum) {
369            size = maximum;
370            preSetup();
371            /* Sub-class will do postSetup */
372        }
373    
374        protected native void enterMemBlock(RealtimeThread rt, MemAreaStack mas);
375        protected native void exitMemBlock(RealtimeThread rt, MemAreaStack mas);
376        protected native void throwIllegalAssignmentError(Object obj, 
377                                                          MemoryArea ma)
378            throws IllegalAssignmentError;
379    
380        /** */
381    
382        protected native MemoryArea shadow();
383    
384        /** */
385      
386        protected native void registerFinal();
387      
388        /** */
389    
390        public void bless(java.lang.Object obj) { 
391            obj.memoryArea = shadow;
392        }
393        
394        /** Create a new array, allocated out of this MemoryArea. 
395         */
396        
397        public Object newArray(final Class type,
398                               final int[] dimensions) 
399            throws IllegalAccessException, OutOfMemoryError
400        {
401            RealtimeThread.checkInit();
402            RealtimeThread rt = RealtimeThread.currentRealtimeThread();
403            for (int i = 0; i<dimensions.length; i++) {
404                if (dimensions[i]<0) {
405                    throw new NegativeArraySizeException();
406                }
407            }
408            rt.memoryArea().checkNewInstance(shadow);
409            Object o;
410            (o = newArray(rt, type, dimensions)).memoryArea = shadow;
411            return o;
412        }
413    
414        /** Check to see if this object can be accessed from this MemoryArea
415         */
416    
417        public void checkAccess(Object obj) {
418            if ((obj != null) && (obj.memoryArea != null) && 
419                obj.memoryArea.scoped) {
420                throwIllegalAssignmentError(obj, obj.memoryArea);
421            }
422        }
423        
424        /** Check access restrictions for a newInstance
425         */
426    
427        public void checkNewInstance(MemoryArea mem) {
428            if (mem.scoped) {
429                throw new IllegalAssignmentError("Illegal assignment during new instance!");
430            }
431        }
432    
433        /** Get the outerScope of this MemoryArea, for non-ScopedMemory's,
434         *  this defaults to null.
435         */
436    
437        public MemoryArea getOuterScope() {
438            return null;
439        }
440        
441        /** Output a helpful string describing this MemoryArea.
442         */
443    
444        public String toString() {
445            return String.valueOf(id);
446        }
447    }