Author Topic: OW, JNI (GCC built java 1.6) and parameters  (Read 1348 times)

Jan-Erik Lärka

  • Global Moderator
  • Full Member
  • *****
  • Thank You
  • -Given: 8
  • -Receive: 15
  • Posts: 198
    • View Profile
OW, JNI (GCC built java 1.6) and parameters
« 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;
}

Lars

  • Sr. Member
  • ****
  • Thank You
  • -Given: 0
  • -Receive: 45
  • Posts: 492
    • View Profile
Re: OW, JNI (GCC built java 1.6) and parameters
« Reply #1 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.

Jan-Erik Lärka

  • Global Moderator
  • Full Member
  • *****
  • Thank You
  • -Given: 8
  • -Receive: 15
  • Posts: 198
    • View Profile
Re: OW, JNI (GCC built java 1.6) and parameters
« Reply #2 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

Lars

  • Sr. Member
  • ****
  • Thank You
  • -Given: 0
  • -Receive: 45
  • Posts: 492
    • View Profile
Re: OW, JNI (GCC built java 1.6) and parameters
« Reply #3 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
« Last Edit: March 29, 2019, 08:32:18 am by Lars »

Dave Yeo

  • Hero Member
  • *****
  • Thank You
  • -Given: 11
  • -Receive: 167
  • Posts: 2339
    • View Profile
Re: OW, JNI (GCC built java 1.6) and parameters
« Reply #4 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);

Lars

  • Sr. Member
  • ****
  • Thank You
  • -Given: 0
  • -Receive: 45
  • Posts: 492
    • View Profile
Re: OW, JNI (GCC built java 1.6) and parameters
« Reply #5 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)

Gregg Young

  • Jr. Member
  • **
  • Thank You
  • -Given: 1
  • -Receive: 6
  • Posts: 55
    • View Profile
Re: OW, JNI (GCC built java 1.6) and parameters
« Reply #6 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.

Jan-Erik Lärka

  • Global Moderator
  • Full Member
  • *****
  • Thank You
  • -Given: 8
  • -Receive: 15
  • Posts: 198
    • View Profile
Re: OW, JNI (GCC built java 1.6) and parameters
« Reply #7 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

Jan-Erik Lärka

  • Global Moderator
  • Full Member
  • *****
  • Thank You
  • -Given: 8
  • -Receive: 15
  • Posts: 198
    • View Profile
Re: OW, JNI (GCC built java 1.6) and parameters
« Reply #8 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;                   \
}

Lars

  • Sr. Member
  • ****
  • Thank You
  • -Given: 0
  • -Receive: 45
  • Posts: 492
    • View Profile
Re: OW, JNI (GCC built java 1.6) and parameters
« Reply #9 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).



       
« Last Edit: April 07, 2019, 08:50:38 pm by Lars »