| C H A P T E R 4 |
This chapter provides detailed advice on porting the threading system.
For a discussion of the architecture of the threading system, refer to the CLDC HotSpot Implementation Architecture Guide.
A decision on coding style must be made: either code for non-blocking scheduling or for hybrid threading. See the following sections for details.
CLDC HotSpot Implementation offers two alternative coding styles for writing the equivalent of a blocking native method:
Non-blocking scheduling - The native method de-schedules its lightweight thread (LWT) until another part of the virtual machine determines that the native method can be executed without blocking. Then the native method is reentered to proceed with the given problematic call that is now guaranteed to be of sufficiently short duration.
Hybrid threading - The native method de-schedules its LWT after delegating the task to an OS thread to execute the given blocking call. When this OS thread, which is truly concurrent to the rest of the virtual machine, completes the call, it causes the LWT to resume. The LWT then reenters the native method to fetch the results of the blocking call.
If you port to another platform, it might be the case that only one of the styles can be implemented. Non-blocking scheduling depends on the existence of functions that can determine whether a subsequent call would block. Take for example the select() function for BSD sockets that can be used to determine whether a socket is ready for a non-blocking data transmission call. Hybrid threading requires that several OS threads are available and that all the blocking OS calls that you want to employ are reentrant.
There is no targeted platform where neither of the required set of functionalities exist.
If available, both styles can be used together in the same configuration and profile, but not mixed within the same native method. Each style has a separate interface that acts as an extension of KNI.
If for a given native method you happen to have a choice between the two styles, non-blocking scheduling is usually preferable for these reasons:
You can allocate parameter space without fixed predetermined space limit.
You can avoid copying into and out of extra buffers by accessing the contents of heap objects directly.
Hybrid threading has these advantages:
It presents less risk that the whole virtual machine block in the native method due to a programming mistake by you or by the implementers of the blocking call.
If the operation in question might take a relatively long time in any event, then the OS’s preemptive scheduling can be employed to prevent unwanted application pauses. Thus, hybrid threading is an option for long-running routines that do not actually block and that for some reason are not broken into parts (for example, because you do not have source code access).
Both styles have a useful shortcut when there is a non-blocking variant of a blocking operation. Call the non-blocking variant first, and in case it already achieves a final result, immediately complete the native method.
In the non-blocking scheduling style, this approach can even be carried further so that all operations in a given native method are non-blocking. The native method’s rescheduling can occur multiple times to come to the final desired result. You can even cascade intermediate results from several non-blocking calls. You can exploit many more variations of this kind.
See sni.h for detailed descriptions of the functions in the Synchronous Native Interface (SNI). sni.h is located in src/vm/share/natives.
Non-blocking scheduling requires the following measures:
If a native method cannot finish its operation (for example, no data are available from a socket), it suspends the operation by calling SNI_BlockThread() and returning immediately. In this case, the thread is placed on the blocked threads list, and any return code from this method is ignored.
You must implement the function JVMSPI_CheckEvents(). This function is called periodically by the virtual machine. It checks if any of the blocked threads are ready for execution and it calls SNI_UnblockThread() on those threads that are ready.
When a blocked thread becomes ready, the suspended native method is executed again to finish its intended operation.
Usually you can have multiple threads that are blocked at the same time for different reasons. For example, two threads are blocked on socket operations and another thread is blocked on user input. You can use the SNI_AllocateReentryData() function to store information about the resources for which a blocked thread is waiting.
When JVMSPI_CheckEvents() is called, it can find the information saved by each blocked thread in the blocked_threads array. JVMSPI_CheckEvents() tries to do a concurrent wait on all the resources specified by the blocked threads. You can achieve much better battery efficiency by avoiding polling.
See ani.h for detailed descriptions of the functions in the Asynchronous Native Interface (ANI). ani.h is located in src/anilib/share.
Hybrid threading requires the following measures:
Always call ANI_Start() at the beginning of your native method to acquire resources for the Hybrid Threading style, in particular an OS thread and parameter memory space.
Look up the shared, location-fixed parameter space for this operation by calling ANI_GetParameterBlock(). If the return value is NULL, the native method is in its first activation. You can then allocate parameter space using ANI_AllocateParameterBlock() and populate the returned space with your input parameter data. When the native method is in its second activation, the return value of ANI_GetParameterBlock() is a pointer to the space allocated as indicated previously and it is time to collect the results of the blocking call from it.
Write a static C function that takes the pointer to the allocated shared parameter space as argument and implements your blocking call. Pass this function to ANI_UseFunction(). This sets up the associated OS thread to execute your function.
Suspend the LWT by calling ANI_BlockThread() and returning immediately. Any return code from this method is ignored and the LWT is de-scheduled for now.
Once the OS thread executes the function you set up with the parameter you allocated, it awakens the LWT, which reenters the native method.
At the end of your native function, before returning with a final result, call ANI_End() to release these resources upon completion, and only then, in case ANI_BlockThread() is called before reaching to ANI_End(), the latter does nothing.
Note - For now, please ignore the is_non_blocking parameter in my_blocking_function(). See ani.h for a detailed discussion of each function. |
Copyright © 2008, Sun Microsystems, Inc. All rights reserved.