JamaicaVM -- User Documentation: The Virtual Machine for Real-time and Embedded Systems | ||
---|---|---|
Prev | Chapter 9. The Jamaica Binary Interface | Next |
When using JBI, the author of native code must write extra code that permits thread preemption and that informs the garbage collector on references onto the Java heap that are used within native code. Failure to do so can cause unlimited thread preemption delays that will destroy the real-time capabilities of the code and dangling references.
To allow thread preemption, JBI code must contain frequent uses of the macro JAMAICA_SYNCHRONIZATION_POINT(ct). This macro should be added
at the beginning and end of every method (unless this method does not call any other methods and has short and bounded execution time),
to every loop body, and
into all stretches of code that may require more time to execute than the tolerable thread preemption time for the traget application.
The execution of the macro JAMAICA_SYNCHRONIZATION_POINT(ct) is very efficient, only in the case in which a thread switch to a higher priority thread actually occurs a higher overhead is required.
At a synchronization point, other Java threads might become active and may cause garbage collector activity. Consequently, all reference values that may be used by the native code after the synchronization point must have been saved for the garbage collector (see below).
Native code that uses the JBI interface and that may cause unpredictable long execution without being able to add synchronization points as described above must be executed asynchronously. The reason may be, e.g., calls to library routines or legacy code or calls that block the current thread waiting for an external event.
Such calls have to be performed asynchrounous from the rest of the virtual machine threads, i.e., these calls must permit other Java threads to execute even though no synchronization points are present. Two macoros JAMAICA_UNSYNC(ct) and JAMAICA_SYNC(ct) are available to enclose asynchronous sections of code.
Example: A call to a native functions readInput() may be made as follows:
int result; [...] JAMAICA_UNSYNC(ct) { result = readInput(); } JAMAICA_SYNC(ct); |
The use of braces ("{") is recommended style to ensure proper nesting, i.e., that every call to JAMAICA_UNSYNC(ct) is followed by a call to JAMAICA_SYNC(ct).
Within code sequences that are asynchronous, accesses to Java objects or calls of any routines or macros of the JBI interface (with the exception of JAMAICA_SYNC(ct), of course) are not allowed and may cause an inconsistent state for the virtual machine or the Java application. Extreme care is consequently required within asynchonous code.
Within asynchronous code, other Java threads might become active and may cause garbage collector activity. Consequently, all reference values that may be used by the native code in or after the asynchronous section must be saved for the garbage collector before the call of JAMAICA_UNSYNC(ct) (see next section).
All references that refer to objects on the garbage collected heap and that are in use by native code using the JBI interface must be known by the garbage calloctor. Unlike JNI, where references are registered automatically, this falls into the responsibility of the developer of the native code when JBI is used.
All reference to Java objects and arrays or to internal structures of the Jamaica virtual machine such as class, method or field descriptors refer to objects on the garbage collected heap. All these reference values are of type jamaica_ref.
The best approach is to save all locally used references, even though this might not be required for references with a short lifespan that can be proven not to cause any garbage collection activity. Any reference whose lifespan includes any of the following needs to be known by the garbage collector:
A synchronization point (JAMAICA_SYNCHRONIZATION_POINT(ct))
An asynchronous code section (JAMAICA_UNSYNC(ct))
A call to any internal macro or routine of the JamaicaVM
A call to any other native or Java routine that may include any of the actions in this list.
To safe a reference such that it is known by the garbage collector, a call to Jamaica_SaveLocal(ct,reference) is required. To release the refence, a corresponding call to Jamaica_ReleaseLocal(ct) has to be made at the end of the lifespan of the reference variable.
These routines use a stack to store these references. Consequently, saving and releasing of references must be nested properly. Also, the available memory on the stack is limited. A call to JAMAICA_STACKCHECK(ct,n) can be used to ensure that the stack has n slots available.
The following code illustrates how reference safing can be performed in an JBI method.
jamaica_ref Jam_Test_example(T*ct) { jamaica_ref obj1, obj2, result; obj1 = ...; obj2 = ...; Jamaica_SaveLocal(ct,obj1); { Jamaica_SaveLocal(ct,obj2); { JAMAICA_UNSYNC(ct) { if (callToLegacyMethod() == 0) { result = obj1; } else { result = obj2; } } JAMAICA_SYNC(ct); } Jamaica_ReleaseLocal(ct); /* obj2 */ } Jamaica_ReleaseLocal(ct); /* obj1 */ return result; } |
Note that arguments to a JBI method that are of reference type are also need to be saved by the JBI code, they are not protected from being garbage collected as they are when JNI is used.