C H A P T E R  4

Porting the Threading System

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.


4.1 Coding Styles for Long-Running Native Methods

CLDC HotSpot Implementation offers two alternative coding styles for writing the equivalent of a blocking native method:

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:

Hybrid threading has these advantages:

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.


4.2 Non-Blocking Scheduling Coding Style

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:

Here is an example:


EXAMPLE 4-1   Coding with the Non-Blocking Scheduling Style
struct EventInfo {
    /* Some information that we need pass from an aborted
     * event reading operation to JVMSPI_CheckEvents() */
};
 
KNIEXPORT KNI_RETURNTYPE_INT
Java_com_sun_midp_lcdui_Events_readInt(void) {
    if (events_are_ready()) {
        KNI_ReturnInt(read_event());
    } else {
        EventInfo *myinfo = \
            SNI_AllocateReentryData(sizeof(EventInfo));
        init(myinfo);
        SNI_BlockThread();
        KNI_ReturnInt(0); /* return value ignored */
    }
}
 
void JVMSPI_CheckEvents(JVMSPI_BlockedThreadInfo * blocked_threads,
                        int num_threads, jlong timeout64) {
    /* gather information about all the blocked threads
     * from blocked_threads */
 
    wait_for_event_or_timeout(timeout64);
 
    if ((a blocked thread was trying to read events) &&
        (events are ready)) {
 
        int i = (index for the blocked event thread);
        SNI_UnblockThread(blocked_threads[i].thread_id);
    }
}
The signatures of the functions discussed above are: 
jboolean SNI_BlockThread(); 
void SNI_UnblockThread(JVMSPI_ThreadID thread_id); 
void JVMSPI_CheckEvents(JVMSPI_BlockedThreadInfo * blocked_threads,
                         int n, jlong timeout); 
void *SNI_AllocateReentryData(size_t reentry_data_size); 
typedef struct {
    JVMSPI_ThreadID thread_id;
    void *reentry_data;
    size_t reentry_data_size;
} JVMSPI_BlockedThreadInfo; 

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.


4.3 Hybrid Threading Coding Style

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:

Here is an example:



Note - For now, please ignore the is_non_blocking parameter in my_blocking_function(). See ani.h for a detailed discussion of each function.




EXAMPLE 4-2   Coding a Potentially Blocking Native Function
typedef struct {
    Handle handle;
    Input *input_buffer;
    size_t input_size;
    Output *output_buffer;
    size_t output_size;
    int result;
} MyParameter;
static jboolean my_blocking_function(void *parameter,
                                      jboolean is_non_blocking) {
  MyParameter *p = (MyParameter *) ANI_GetParameterBlock();
  int result = do_this_and_that(p->handle, &p->input_buffer,
                                 p->input_size, &p->output_buffer,
                                 &p->output_size)
  return KNI_TRUE;
}
KNIEXPORT KNI_RETURNTYPE_INT
Java_com_sun_myProfile_doThisAndThat(void) {
    if (!ANI_Start()) {
      ... // not enough resources
      KNI_ReturnInt(-1);
}
MyParameter *p = (MyParameter *) ANI_GetParameterBlock();
    if (p == NULL) { // first round: set up input and function
  size_t input_size = ...
  size_t output_size = ...
 p = (MyParameter *) 
 ANI_AllocateParameterBlock(sizeof(MyParameter) + output_size);
+ input_size p->handle = ...
  p->input_buffer = (Input *) (p + 1);
  p->input_size = input_size;
  KNI_StartHandles(1);
  KNI_DeclareHandle(input_object);
  KNI_GetParameterAsObject(1, input_object);
      KNI_GetRawArrayRegion(input_object, 0, p->input_size, 
                           (jbyte *) p->input_buffer);
      KNI_EndHandles();
 
  p->output_buffer = (Output *) ((char *) 
(p + 1) + input_size);
  p->output_size = output_size;
      p->result = -1;
  ANI_UseFunction(my_blocking_function);
  ANI_BlockThread();
} else { // second round
  KNI_StartHandles(1);
  KNI_DeclareHandle(output_object);
  KNI_GetParameterAsObject(2, output_object);
      KNI_SetRawArrayRegion(output_object, 0, p->output_size, 
                           (jbyte *) p->output_buffer);
      KNI_EndHandles();
   }
int result = p->result;
ANI_End();
    KNI_ReturnInt(result);
}