001    // ScopedMemory.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    /**
007     * @author Wes Beebee <<a href="mailto:wbeebee@mit.edu">wbeebee@mit.edu</a>>
008     */
009    
010    /** <code>ScopedMemory</code> is the abstract base class of all classes dealing
011     *  with representations of memoryspaces with a limited lifetime. The
012     *  <code>ScopedMemory</code> area is valid as long as there are real-time
013     *  threads with access to it. A reference is created for each accessor when
014     *  either a real-time thread is created with the <code>ScopedMemory</code>
015     *  object as its memory area, or a real-time thread runs the <code>enter()</code>
016     *  method for the memory area. When the last reference to the object is removed,
017     *  by exiting the thread or exiting the <code>enter()</code> method, finalizers
018     *  are run for all objects in the memory area, and the area is emptied.
019     *  <p>
020     *  A <code>ScopedMemory</code> area is a connection to a particular region of
021     *  memory and reflects the current status of it.
022     *  <p>
023     *  When a <code>ScopedMemory</code> area is instantiated, the object itself is allocated
024     *  from the current memory allocation scheme in use, but the memory space that object
025     *  represents is not.  Memory is allocated, as always, using an <code>RTJ_malloc</code> call.
026     *  <p>
027     *  The <code>enter()</code> method of <code>ScopedMemory</code> is the mechanism used
028     *  to activate a new memory scope. Entry into the scope is done by calling the method
029     *  <code>enter(Runnable r)</code>, where <code>r</code> is a <code>Runnable</code>
030     *  object whose <code>run()</code> method represents the entry point to the code that
031     *  will run in the new scope. Exit from the scope occurs when the <code>r.run()</code>
032     *  completes. Allocations of objects within <code>r.run()</code> are done with the
033     *  <code>ScopedMemory</code> area. When <code>r.run()</code> is complete, the scoped
034     *  memory area is no longer active. Its reference count will be decremented and if it
035     *  is zero all of objects in the memory are finalized and collected.
036     *  <p>
037     *  Objects allocated from a <code>ScopedMemory</code> area have a unique lifetime. They
038     *  cease to exist on exiting <code>enter()</code> method or upon exiting the last
039     *  real-time thread referencing the area, regardless of any references that may exist
040     *  to the object. Thus, to maintain the safety of Java and avoid dangling references,
041     *  a very restrictive set of rules apply to <code>ScopedMemory</code> area objects:
042     *  <ul>
043     *  <li>1. A reference to an object in <code>ScopedMemory</code> can never be stored in
044     *         an Object allocated in the Java heap.
045     *  <li>2. A reference to an object in <code>ScopedMemory</code> can never be stored in
046     *         an Object allocated in <code>ImmortalMemory</code>.
047     *  <li>3. A reference to an object in <code>ScopedMemory</code> can only be stored in
048     *         Objects allocated in the same <code>ScopedMemory</code> area, or into a
049     *         -- more inner -- <code>ScopedMemory</code> area nested by the use of its
050     *         <code>enter()</code> method.
051     *  <li>4. References to immortal of heap objects <i>may</i> be stored into an object
052     *         allocated in a <code>ScopedMemory</code> area.
053     *  </ul>
054     */
055    public abstract class ScopedMemory extends MemoryArea {
056        protected Object portal;
057        private int count = 0;
058    
059        /** <code>logic.run()</code> is executed when <code>enter()</code> is called. */
060        protected Runnable logic;
061    
062        /** Create a new <code>ScopedMemory</code> with the given parameters.
063         *
064         *  @param size The size of the new <code>ScopedMemory</code> area in bytes.
065         *              If size is less than or equal to zero nothing happens.
066         */
067        public ScopedMemory(long size) {
068            super(size);
069            portal = null;
070            scoped = true;
071        }
072    
073        /** Create a new <code>ScopedMemory</code> with the given parameters.
074         *
075         *  @param size The size of the new <code>ScopedMemory</code> area in bytes.
076         *              If size is less than or equal to zero nothing happens.
077         *  @param r The logic which will use the memory represented by <code>this</code>
078         *           as its initial memory area.
079         */
080        public ScopedMemory(long size, Runnable r) {
081            this(size);
082            logic = r;
083        }
084    
085        /** Create a new <code>ScopedMemory</code> with the given parameters.
086         *
087         *  @param size The size of the new <code>ScopedMemory</code> area estimated
088         *              by an instance of <code>SizeEstimator</code>.
089         */
090        public ScopedMemory(SizeEstimator size) {
091            this(size.getEstimate());
092        }
093    
094        /** Create a new <code>ScopedMemory</code> with the given parameters.
095         *
096         *  @param size The size of the new <code>ScopedMemory</code> area estimated
097         *              by an instance of <code>SizeEstimator</code>.
098         *  @param r The logic which will use the memory represented by <code>this</code>
099         *           as its initial memory area.
100         */
101        public ScopedMemory(SizeEstimator size, Runnable r) {
102            this(size);
103            logic = r;
104        }
105    
106        public ScopedMemory(long minimum, long maximum) {
107            super(minimum, maximum);
108            portal = null;
109            scoped = true;
110        }
111    
112        /** Associate this <code>ScopedMemory</code> area to the current realtime
113         *  thread for the duration of the execution of the <code>run()</code> method
114         *  of the current instance of <code>Schedulable</code> or the <code>run()</code>
115         *  method of the instance of <code>Schedulable</code> fiven in the constructor.
116         *  During this bound period of execution, all objects are allocated from the
117         *  <code>ScopedMemory</code> area until another one takes effect, or the
118         *  <code>enter()</code> method is exited. A runtime exception is thrown if this
119         *  method is called from a thread other than a <code>RealtimeThread</code> or
120         *  <code>NoHeapRealtimeThread</code>.
121         */
122        public void enter() {
123            // Will NEVER implement single parent rule.
124            super.enter();
125        }
126    
127        /** Associate this <code>ScopedMemory</code> area to the current realtime
128         *  thread for the duration of the execution of the <code>run()</code> method
129         *  of the current instance of <code>Schedulable</code> or the <code>run()</code>
130         *  method of the instance of <code>Schedulable</code> fiven in the constructor.
131         *  During this bound period of execution, all objects are allocated from the
132         *  <code>ScopedMemory</code> area until another one takes effect, or the
133         *  <code>enter()</code> method is exited. A runtime exception is thrown if this
134         *  method is called from a thread other than a <code>RealtimeThread</code> or
135         *  <code>NoHeapRealtimeThread</code>.
136         *
137         *  @param logic The runnable object which contains the code to execute.
138         */
139        public void enter(Runnable logic) {
140            // Will NEVER implement single parent rule.
141            super.enter(logic);
142        }
143    
144        /** Get the maximum size this memory area can attain. If this is a fixed size
145         *  memory area, the returned value will be equal to the initial size.
146         *
147         *  @return The maximum size attainable.
148         */
149        public long getMaximumSize() { 
150            return size;
151        }
152        
153        /** Return a reference to the portal object in this instance of <code>ScopedMemory</code>.
154         *
155         *  @return A reference to the portal object or null if there is no portal object.
156         */
157        public Object getPortal() {
158            return portal;
159        }
160    
161        /** Returns the reference count of this <code>ScopedMemory</code>. The reference
162         *  count is an indication of the number of threads that may have access to this scope.
163         *
164         *  @return The reference count of this <code>ScopedMemory</code>.
165         */
166        public int getReferenceCount() {
167            return count;
168        }
169    
170        /** Wait until the reference count of this <code>ScopedMemory</code> goes down to zero.
171         *
172         *  @throws java.lang.InterruptedException If another thread interrupts this thread
173         *                                         while it is waiting.
174         */
175        public void join() throws InterruptedException {
176            // TODO
177        }
178    
179        /** Wait at most until the time designated by the <code>time</code> parameter for the
180         *  reference count of this <code>ScopedMemory</code> to go down to zero.
181         *
182         *  @param time If this time is an absolute time, the wait is bounded by that point
183         *              in time. If the time is a relative time (or a member of the
184         *              <code>RationalTime</code> subclass of <code>RelativeTime</code>) the
185         *              wait is bounded by the specified interval from some time between the
186         *              time <code>join<code> is called and the time it starts waiting for
187         *              the reference count to reach zero.
188         *  @throws java.lang.InterruptedException If another thread interrupts this thread
189         *                                         while it is waiting.
190         */
191        public void join(HighResolutionTime time)
192            throws InterruptedException {
193            // TODO
194        }
195    
196        /** Combine <code>join()</code> and <code>enter()</code> such that no <code>enter()</code>
197         *  from another thread can intervene between the two method invocations. The resulting
198         *  method will wait for the reference count on this <code>ScopedMemory</code> to reach
199         *  zero, then enter the <code>ScopedMemory</code> and execute the <code>run()</code>
200         *  method from <code>logic</code> passed in the constructor. if no <code>Runnable</code>
201         *  was passed, the maethod returns immediately.
202         *
203         *  @throws java.lang.InterruptedException If another thread interrupts this thread while
204         *                                         it is waiting.
205         */
206        public void joinAndEnter() throws InterruptedException {
207            joinAndEnter(this.logic);
208        }
209    
210        /** Combine <code>join()</code> and <code>enter()</code> such that no <code>enter()</code>
211         *  from another thread can intervene between the two method invocations. The resulting
212         *  method will wait for the reference count on this <code>ScopedMemory</code> to reach
213         *  zero, then enter the <code>ScopedMemory</code> and execute the <code>run()</code>
214         *  method from <code>logic</code> passed in the constructor. if no <code>Runnable</code>
215         *  was passed, the maethod returns immediately.
216         *
217         *  @param time The time that bounds the wait.
218         *  @throws java.lang.InterruptedException If another thread interrupts this thread while
219         *                                         it is waiting.
220         */
221        public void joinAndEnter(HighResolutionTime time)
222            throws InterruptedException {
223            joinAndEnter(this.logic, time);
224        }
225    
226        /** Combine <code>join()</code> and <code>enter()</code> such that no <code>enter()</code>
227         *  from another thread can intervene between the two method invocations. The resulting
228         *  method will wait for the reference count on this <code>ScopedMemory</code> to reach
229         *  zero, then enter the <code>ScopedMemory</code> and execute the <code>run()</code>
230         *  method from <code>logic</code> passed in the constructor. if no <code>Runnable</code>
231         *  was passed, the maethod returns immediately.
232         *
233         *  @param logic The <code>java.lang.Runnable</code> object which contains the code to execute.
234         *  @throws java.lang.InterruptedException If another thread interrupts this thread while
235         *                                         it is waiting.
236         */
237        public void joinAndEnter(Runnable logic) throws InterruptedException {
238            // TODO
239        }
240    
241        /** Combine <code>join()</code> and <code>enter()</code> such that no <code>enter()</code>
242         *  from another thread can intervene between the two method invocations. The resulting
243         *  method will wait for the reference count on this <code>ScopedMemory</code> to reach
244         *  zero, then enter the <code>ScopedMemory</code> and execute the <code>run()</code>
245         *  method from <code>logic</code> passed in the constructor. if no <code>Runnable</code>
246         *  was passed, the method returns immediately.
247         *
248         *  @param logic The <code>java.lang.Runnable</code> object which contains the code to execute.
249         *  @param time The time that bounds the wait.
250         *  @throws java.lang.InterruptedException If another thread interrupts this thread while
251         *                                         it is waiting.
252         */
253        public void joinAndEnter(Runnable logic, HighResolutionTime time)
254            throws InterruptedException {
255            // TODO
256        }
257    
258        /** Set the argument to the portal object in the memory area represented by this instance
259         *  of <code>ScopedMemory</code>.
260         *
261         *  @param object The object which will become the portal for <code>this</code>. If null
262         *                the previous portal object remains the portal object for <code>this</code>
263         *                or if there was no previous portal object then there is still no
264         *                portal object for <code>this</code>.
265         */
266        public void setPortal(Object object) {
267            if (this.equals(MemoryArea.getMemoryArea(object))) portal = object;
268        }
269    
270        /** Returns a user-friendly representation of this <code>ScopedMemory</code>.
271         *
272         *  @return The string representation.
273         */
274        public String toString() {
275            return "ScopedMemory: " + super.toString();
276        }
277    
278        /** Get the MemoryArea which contains this ScopedMemory for
279         *  the current RealtimeThread.
280         */
281        public MemoryArea getOuterScope() {
282            return RealtimeThread.currentRealtimeThread().outerScope(this);
283        }
284    
285        /** Check to see if this ScopedMemory can have access to 
286         *  the given object.
287         */
288        public void checkAccess(Object obj) { 
289            //      Stats.addCheck();
290            if (obj != null) {
291                MemoryArea target = getMemoryArea(obj);
292                if ((this != target) && target.scoped &&
293                    (!RealtimeThread.currentRealtimeThread()
294                     .checkAccess(this, target))) {
295                    throwIllegalAssignmentError(obj, target);
296                }
297            }           
298        }
299    
300        /** Check to see if a newInstance can go through 
301         */
302        public void checkNewInstance(MemoryArea mem) {
303            if ((this != mem) && mem.scoped &&
304                (!RealtimeThread.currentRealtimeThread()
305                 .checkAccess(this, mem))) {
306                throw new IllegalAssignmentError("Illegal assignment during new instance!");
307            }
308        }
309    
310        /** Cannot call this on a ScopedMemory (doesn't cleanup MemBlocks 
311         *  appropriately).  Should never need to, since that'll cause
312         *  an access violation according to the spec. 
313         */
314        protected void setupMemBlock(RealtimeThread rt) 
315            throws IllegalAccessException {
316            throw new IllegalAccessException();
317        }
318    }