OS2 World Community Forum

OS/2, eCS & ArcaOS - Technical => Programming => Topic started by: Jan-Erik Lärka on March 28, 2019, 06:20:23 am

Title: OW, JNI (GCC built java 1.6) and parameters
Post by: Jan-Erik Lärka on March 28, 2019, 06:20:23 am
Hello,
I've now looked into how to use java 1.6 and with the help of the answer to the issue I posted quite some time ago, now managed to turn off floating point instructions.
Would appreciate if you can tell me how to also overcome the problem to feed the java code with one or more parameter(s).

LoadLib has been tested to dynamically load and use the dll for SQLite3 and Cairo, both the OW and GCC builds (the gcc builds installed with current ANPM) without recompile in between. The example code I used with SQLite3 and Cairo loaded with parameter "false" as they don't need the floating point fix. :-)
Try with OW as you like, but don't blame me if you run into problems.


Ohh, anyone that would like to help me to write code in vxrexx for this?
A gui to use java in the background etc.

Code: [Select]
#define INCL_DOS
#define INCL_DOSMODULEMGR
#define INCL_DOSMISC
#define INCL_DOSERRORS
#include <os2.h>
#include <stdio.h>
#include <ctype.h>
#include <jni.h>
#include <stdlib.h>
#include <iostream>
#include "LoadLib.hpp"

LoadLib *javm;
ULONG rc;

JNIEnv* create_vm() {
    javm = new LoadLib( "client\\jvm" );//, ".", false ); //when used with sqlite3 and cairo
    if( javm->rc == NO_ERROR ) {
        JavaVM* jvm;
        JNIEnv* env;

        JavaVMOption options[] = {
            { "-Xmx512m", NULL },
            { "-XX:+TraceClassLoading", NULL },//To see the origin of the problem class
            { "-Djava.class.path=.", NULL },  //D:\\Programs\\vxrexx\\projects\\ODBCADMN\\j4o";
            { "-Djava.library.path=.", NULL },// set native library path
            { "-Djava.compiler=NONE", NULL }, // disable JIT
            { "-verbose:gc", NULL },          // print gc messages
            { "-verbose:jni", NULL },         // print JNI-related messages
            { "-verbose:class", NULL }        // print class loading messages
        };

        JavaVMInitArgs args;
        args.version = JNI_VERSION_1_6;
        args.nOptions = sizeof( options ) / sizeof( JavaVMOption );
        args.options = options;
        args.ignoreUnrecognized = JNI_FALSE;

        InitFunc( javm->hmod, jint, JNI_CreateJavaVM, ( JavaVM **p_vm, void **p_env, void *vm_args ) );
        jint jrc = JNI_CreateJavaVM( &jvm, (void**)&env, &args );
//#ifdef __WATCOMC__
        if( jrc != JNI_OK )
            cerr << "Problems loading JVM" << endl;
        else
            cout << "JVM has been loaded sucessfully" << endl;
//#endif
        delete options;
        return env;
    }
    return NULL;
}

void invoke_class( JNIEnv* env ) {
  cout << "invoke class" << endl;

  jclass helloWorldClass;
  jmethodID mainMethod;
  jobjectArray applicationArgs;
  jstring applicationArg0;

  cout << "FindClass HelloWorld" << endl;
  helloWorldClass = env->FindClass( "HelloWorld" );

  cout << "main helloWorld" << endl;
  mainMethod = env->GetStaticMethodID( helloWorldClass, "main", "([Ljava/lang/String;)V" );

  cout << "New Args" << endl;
  applicationArgs = env->NewObjectArray( 1, env->FindClass( "java/lang/String" ), NULL );
  cout << "New UTF String to test parameters" << endl;
  applicationArg0 = env->NewStringUTF( "testparam" );
  cout << "Args" << endl;
  env->SetObjectArrayElement( applicationArgs, 0, applicationArg0 ); //KABOOM! Unless commented out. :-(

  cout << "void main" << endl;
  env->CallStaticVoidMethod( helloWorldClass, mainMethod, applicationArgs);
}

int main( int argc, char **argv ) {
    JNIEnv* env = create_vm();
    if( env )
        invoke_class( env );
    else
        return 1;
  return 0;
}
Title: Re: OW, JNI (GCC built java 1.6) and parameters
Post by: Lars on March 28, 2019, 03:27:04 pm
Sorry I cannot help with your initial problem but you cannot use DosSetExceptionHandler / DosUnsetExceptionHandler the way you do because the exception registration record ("ex") has to be placed on the stack.
For what you did, it'll go into the heap. You will have to expose this parameter (address) at the constructor so that you can set it in the constructor.
Title: Re: OW, JNI (GCC built java 1.6) and parameters
Post by: Jan-Erik Lärka on March 28, 2019, 10:13:57 pm
Interesting...

as I received "divide by zero" messages without it, the initial problem went away and was replaced by "inexact result" as I added the case for divide by zero, but all float-related went away as I added the other XCPT_FLOAT_... cases.

Would appreciate if you could give me a brief example how it should be resolved.

Regards,
//Jan-Erik
Title: Re: OW, JNI (GCC built java 1.6) and parameters
Post by: Lars on March 29, 2019, 08:27:22 am
1) catching the exception will work (as you observed). However, you will break the exception handler chaining if you do not place the exception registration record on the stack. That might lead to some odd effects (remember there is a default exception handler that the OS installs in order to catch exceptions in your program).

2) As far as I can tell from your code, the only reason that you need to catch exceptions is so that you can reset the FPU controls flags to turn back off FP exceptions. I am no C++ expert. I see these solutions (from best to worst)
a) have a look at C++ exceptions. I would hope that they allow to catch HW exceptions. But I don't know how you would be able to continue program execution from the point of exception
b) write C macros to set up and remove exception handlers. This is how XWorkplace does it and it works just fine. In the following example, for your case, you will not need to use setjmp/longjmp because you can always continue execution from the point of exception:

typedef struct _EXCEPTIONREGISTRATIONRECORD2
{
    EXCEPTIONREGISTRATIONRECORD reg;
    jmp_buf                                 jmp;
} EXCEPTIONREGISTRATIONRECORD2, *PEXCEPTIONREGISTRATIONRECORD2;

extern ULONG APIENTRY ExceptionHandler(
                                    PEXCEPTIONREPORTRECORD pRep,
                                    PEXCEPTIONREGISTRATIONRECORD2 pReg,
                                    PCONTEXTRECORD pCtx,
                                    PVOID pDispCtx);

#define TRY(except)                                                                     \
{                                                                                              \
    EXCEPTIONREGISTRATIONRECORD2 except={0};                                \
    except.reg.ExceptionHandler = (ERR)ExceptionHandler;                       \
    DosSetExceptionHandler((PEXCEPTIONREGISTRATIONRECORD)&except);   \
    if (setjmp(except.jmp) == 0) {

#define CATCH()                                                                          \
    } else {

#define ENDTRY(except)                                                                     \
    }                                                                                               \
    DosUnsetExceptionHandler((PEXCEPTIONREGISTRATIONRECORD)&except);    \
}

...

ULONG APIENTRY ExceptionHandler(
                                    PEXCEPTIONREPORTRECORD pRep,
                                    PEXCEPTIONREGISTRATIONRECORD2 pReg,
                                    PCONTEXTRECORD pCtx,
                                    PVOID pDispCtx)
{
    if (pRep->fHandlerFlags & (EH_EXIT_UNWIND|EH_UNWINDING|EH_NESTED_CALL))
    {
        return XCPT_CONTINUE_SEARCH;
    }

    switch (pRep->ExceptionNum)
    {
        case XCPT_ACCESS_VIOLATION:
        case XCPT_ILLEGAL_INSTRUCTION:
        case XCPT_PRIVILEGED_INSTRUCTION:
        case XCPT_INVALID_LOCK_SEQUENCE:
        case XCPT_INTEGER_DIVIDE_BY_ZERO:
        case XCPT_INTEGER_OVERFLOW:
        case XCPT_FLOAT_DIVIDE_BY_ZERO:
        case XCPT_FLOAT_OVERFLOW:
        case XCPT_FLOAT_UNDERFLOW:
        case XCPT_FLOAT_INVALID_OPERATION:
        case XCPT_FLOAT_DENORMAL_OPERAND:
        case XCPT_FLOAT_INEXACT_RESULT:
        case XCPT_FLOAT_STACK_CHECK:
            longjmp(pReg->jmp,1);

        default:
            break;
    }
    return XCPT_CONTINUE_SEARCH;
}


c) allocate an exception registration record on the stack and pass the address to your constructor. In the constructor, register the handler and save the address of the exception registration record to a private variable. Then, in the destructor use that address to deregister the exception handler.

Lars
Title: Re: OW, JNI (GCC built java 1.6) and parameters
Post by: Dave Yeo on March 29, 2019, 03:41:47 pm
Is this to work around the FPU Control Word bug? If so, perhaps this, taken from the Cairo init code, and perhaps the reason that Cairo works OK for you.
Code: [Select]
    /* Workaround a bug in some OS/2 PM API calls that
     * modify the FPU Control Word but fail to restore it.
     */
    usCW = _control87 (0, 0);
    usCW = usCW | EM_INVALID | 0x80;
    _control87 (usCW, MCW_EM | 0x80);
Title: Re: OW, JNI (GCC built java 1.6) and parameters
Post by: Lars on April 01, 2019, 06:06:28 pm
I think the problem he tries to catch is that some DLLs keep resetting the FPU control word to what they think is correct. So if you call a routine in those DLLs, the FPU control word will be modified when the call returns (which of course is bad,bad,bad).
Jan-Erik does set the FPU control word to what he wants (namely, to disable all FPU exceptions).
But then he wants to register an exception handler so that if something resets the FPU control word and subsequently an FPU exception occurs he can reset the FPU control word to what he wants (namely, to disable all FPU exceptions)
Title: Re: OW, JNI (GCC built java 1.6) and parameters
Post by: Gregg Young on April 02, 2019, 09:06:53 am
An exception handler for this was added to libcx 0.6.4. Simply linking to libcx should take care of this.
Title: Re: OW, JNI (GCC built java 1.6) and parameters
Post by: Jan-Erik Lärka on April 02, 2019, 10:47:48 pm
This originate from http://trac.netlabs.org/java/ticket/245 and the reply to http://lvzuufx.blogspot.com/2014/09/os2-codes-prevent-sigfpe.html
I like the idea to write once and use everywhere, so the file was my interpretation of the blog post, how to to solve the problem .

OW GUI resemble Borland C++ that was taught out at school where one add the files to compile and it ... figure out how to create the exe.
The #define INITPROC(...) is what I came up with to address the limitation to call gcc built dlls from code compiled with OW.

Lars is quite right, but I can't quite grasp how/where to change the code to get it right. I also downloaded and looked at the code for XWPDeamon, but can't quite see/figure it out from there either.
I would appreciate a direct rewrite of the code to help me out.

Regards,
//Jan-Erik
Title: Re: OW, JNI (GCC built java 1.6) and parameters
Post by: Jan-Erik Lärka on April 07, 2019, 08:20:31 pm
This is what I tried:

Ripped some of the code out and created reusable #define that I call with
AddFPEHandler();
...
main(...) {
    InitNoFPE( javm );
    NoFPE( javm, client\\jvm, . );
...
The problem I face now is that the fpeHandler get called twice followed by a bunch of java class loading/debug lines before it stop (but doesn't end) and use up a cpu core.

How do I get passed that?

Code: [Select]
/* r = return type, f = funtion name, p = parameter(s) */
#define InitFunc( h, r, f, p )                     \
    r( __syscall *f)p;                             \
    rc = DosQueryProcAddr( h, NULL, "_"#f, (PFN *) &f ); /*GCC*/\
    if( rc != 0 )                                  \
        rc = DosQueryProcAddr( h, NULL, #f, (PFN *) &f );/*OW*/

/* v = variable name */
#define InitNoFPE( v )                             \
    EXCEPTIONREGISTRATIONRECORD ex = { 0, };       \
    LoadLib *v = new LoadLib();                    \
    _control87( 0, MCW_EM );                       \
    if( v ) {                                      \
        ex.ExceptionHandler = ( ERR )fpeHandler;   \
        DosSetExceptionHandler( &ex );             \
    }

/* v = variable name, n = name of dll, p = path to dll */
#define NoFPE( v, n, p )                           \
    v->LoadLibrary( #n, #p );                      \
    if( v->rc == NO_ERROR ) {                      \
        _control87( 0, MCW_EM );                   \
        DosSetExceptionHandler( &ex );             \
    }

// this handler was excerpted from Mozilla repository
#define AddFPEHandler()                            \
static ULONG fpeHandler( PEXCEPTIONREPORTRECORD p1,\
                   PEXCEPTIONREGISTRATIONRECORD p2,\
                   PCONTEXTRECORD p3,              \
                   PVOID p4 )                      \
{                                                  \
    switch( p1->ExceptionNum )                     \
    {                                              \
        case XCPT_FLOAT_DENORMAL_OPERAND:          \
        case XCPT_FLOAT_DIVIDE_BY_ZERO:            \
        case XCPT_FLOAT_INEXACT_RESULT:            \
        case XCPT_FLOAT_INVALID_OPERATION:         \
        case XCPT_FLOAT_OVERFLOW:                  \
        case XCPT_FLOAT_STACK_CHECK:               \
        case XCPT_FLOAT_UNDERFLOW:                 \
        {                                          \
            unsigned cw = p3->ctx_env[0];          \
            if( ( cw & MCW_EM ) != MCW_EM ) {      \
            /* Mask out all floating point exceptions */\
                p3->ctx_env[0] |= MCW_EM;          \
            /* Following two lines set precision to 53 bit mantissa. See jsnum.c */\
                p3->ctx_env[0] &= ~MCW_PC;         \
                p3->ctx_env[0] |= PC_53;           \
                printf("----- XCPT_CONTINUE_EXECUTION\n");\
                return XCPT_CONTINUE_EXECUTION;    \
            }                                      \
            break;                                 \
        }                                          \
    }                                              \
    return XCPT_CONTINUE_SEARCH;                   \
}
Title: Re: OW, JNI (GCC built java 1.6) and parameters
Post by: Lars on April 07, 2019, 08:46:44 pm
class LoadLib {
...

public:
    EXCEPTIONREGISTRATIONRECORD *pex;
...
    LoadLib( EXCEPTIONREGISTRATIONRECORD *prec, PSZ pszLibrName = "", PSZ pszLibrPath = ".", bool fpe = true) {
    pex = prec;
    memset(pex,0,sizeof(*pex));
    ...
    pex->ExceptionHandler = ( ERR )fpeHandler;
    DosSetExceptionHandler( pex);
    ...

    ~LoadLib() {
         if (pex->ExceptionHandler)
              DosUnsetExceptionHandler(pex);
     ....


call:

JNIEnv* create_vm(EXCEPTIONREGISTRATIONRECORD *pex) {
    javm = new LoadLib( pex,"client\\jvm" );//, ".", false ); //when used with sqlite3 and cairo
    ...


int main( int argc, char **argv ) {
    EXCEPTIONREGISTRATIONRECORD ex;
    JNIEnv* env = create_vm(&ex);
    ...
    delete javm; <---- your forgot this one ...
    return 0;
}


Just one thing to note: be aware that an exception handler can only be registered for the CURRENT stack frame (and everything called directly or indirectly from this point).
That is, you need to do "delete javm" BEFORE you return from the function where you called "new LoadLibrary". That also means you cannot pass around the LoadLibrary object and then return from the function registering it.

Also be aware that you need to set exception handlers PER THREAD. You cannot do a "new LoadLibrary" and then expect the exception handler to work from another thread (but see above, it's forbidden to pass around the object anyways).



       
Title: Re: OW, JNI (GCC built java 1.6) and parameters
Post by: Jan-Erik Lärka on May 20, 2020, 09:51:15 pm
Hello,

I've managed to find out the problem and solve it.

Built BSF4Rexx (2009) with Open Watcom 2.0 IDE and resolved some calling convention difficulties and it output:
Code: [Select]
Rexx interpreter:  REXXSAA 4.00 3 Feb 1999
BSF4Rexx (DLL/so): 282.20090420 org/rexxla/bsf/engines/rexx

The problem there is now related to something when java call back into the library:
Code: [Select]
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
        at org.rexxla.bsf.engines.rexx.RexxAndJava.javaCallBSF(RexxAndJava.java:1306)

Please feel free to help me out

The code is useful as you try to write code not only for Java6 (OpenJDK) but also SQLite3 and Cairo as well, as it take care of floating point errors that occur there.

//Jan-Erik

LoadLib.txt modified to allow versions to load, such as cario2.dll, cario.dll, cairo164.dll etc.
Title: Re: OW, JNI (GCC built java 1.6) and parameters
Post by: Dave Yeo on May 21, 2020, 03:27:12 am
I'm sure I'm missing something but the Cairo Clock in the Screensaver is an example of OpenWatcom code calling a GCC DLL, cairo2.dll and using floating point to draw the clock. It seems to work fine.
The OW code to include Cairo
Code: [Select]
#define cairo_public __declspec(__cdecl)
#include <cairo\cairo.h>
#include <cairo\cairo-os2.h>
and later has,
Code: [Select]
  // Set FPU state which might have been changed by some Win* calls!
  // (Workaround for a PM bug)
  DisableFPUException();

  // Initialize cairo support
  cairo_os2_init();
with DisableFPUException being,
Code: [Select]
/* One helper function: */
static void inline DisableFPUException()
{
  unsigned short usCW;

  // Some OS/2 PM API calls modify the FPU Control Word,
  // but forgets to restore it.

  // This can result in XCPT_FLOAT_INVALID_OPCODE exceptions,
  // so to be sure, we always disable Invalid Opcode FPU exception
  // before using FPU stuffs with Cairo or from this thread.

  usCW = _control87(0, 0);
  usCW = usCW | EM_INVALID | 0x80;
  _control87(usCW, MCW_EM | 0x80);
}
With the linker fed an Imports.lnk  consisting of the exports,
Code: [Select]
IMPORT _cairo_os2_surface_paint_window     cairo2
IMPORT _cairo_save                         cairo2
IMPORT _cairo_scale                        cairo2
IMPORT _cairo_set_source_rgb               cairo2
IMPORT _cairo_rectangle                    cairo2
IMPORT _cairo_fill                         cairo2
IMPORT _cairo_translate                    cairo2
IMPORT _cairo_set_line_width               cairo2
IMPORT _cairo_move_to                      cairo2
IMPORT _cairo_line_to                      cairo2
IMPORT _cairo_close_path                   cairo2
IMPORT _cairo_stroke                       cairo2
IMPORT _cairo_arc                          cairo2
IMPORT _cairo_select_font_face             cairo2
IMPORT _cairo_set_font_size                cairo2
IMPORT _cairo_text_extents                 cairo2
IMPORT _cairo_text_path                    cairo2
IMPORT _cairo_set_source_rgba              cairo2
IMPORT _cairo_fill_preserve                cairo2
IMPORT _cairo_restore                      cairo2
IMPORT _cairo_os2_init                     cairo2
IMPORT _cairo_os2_surface_create_for_window cairo2
IMPORT _cairo_os2_surface_set_hwnd         cairo2
IMPORT _cairo_create                       cairo2
IMPORT _cairo_destroy                      cairo2
IMPORT _cairo_surface_destroy              cairo2
IMPORT _cairo_os2_fini                     cairo2

The code is here, https://bitbucket.org/dryeo/doodle-screen-saver/src/work-v24/Modules/CairoClock/ (https://bitbucket.org/dryeo/doodle-screen-saver/src/work-v24/Modules/CairoClock/) in CaClock.c

Title: Re: OW, JNI (GCC built java 1.6) and parameters
Post by: Jan-Erik Lärka on May 21, 2020, 09:56:54 am
Nice,

Java was more of a challenge as it need the code fpeHandler that get called as various Floating point exceptions pop up.
Code: [Select]
LoadLib *jVM = new LoadLib( "jvm" ); //find in before/after libpath, path etc.
if( jVM->rc != NO_ERROR ) //jvm.dll not found, try subfolder "client" of above and environment variable JAVA_HOME (if set), try as is if param 2 instead contain \.
       jVM->Load( "client\\jvm", "JAVA_HOME" );

... do other stuff ...
Code: [Select]
/* Define each function dynamically */
    int rc = NO_ERROR; //must be defined as InitFunc use rc
    InitFunc( jVM->hmod, jint, JNI_GetDefaultJavaVMInitArgs, ( void *vm_args ) );
    InitFunc( jVM->hmod, jint, JNI_CreateJavaVM, ( JavaVM **p_vm, JNIEnv **p_env, void *vm_args ) );
InitFunc take care of decorations "_" in front or after.

Code: [Select]
resVer = JNI_GetDefaultJavaVMInitArgs(&vm_args2);...
Code: [Select]
res = JNI_CreateJavaVM( &currentJVM, &jniEnv, &vm_args2);

Also tried with libsane and created my own version of scanimage, that use defaults. There I have problems to reach and set internal structures of variables in the memory created by the gcc compiled library
Title: Re: OW, JNI (GCC built java 1.6) and parameters
Post by: Dave Yeo on May 21, 2020, 04:41:57 pm
Are you using OpenWatcom from openwatcom.org or the Github fork? The code from OpenWatcom has more recent OS/2 fixes including, I believe, some stuff for high memory.
Title: Re: OW, JNI (GCC built java 1.6) and parameters
Post by: Jan-Erik Lärka on May 21, 2020, 07:30:32 pm
I use open-watcom-c-os2-2.0-beta3.zip wherever it may come from.

Ohh, LoadLib.txt should be adjusted ... change a ".dll" to "*dll" to allow it to use any version of the library you define. Especially useful with Cairo as it come in many versions.