IOS initialization crash

112 views
Skip to first unread message

Dave Dyer

unread,
Jul 1, 2016, 12:05:51 PM7/1/16
to CodenameOne Discussions

I interpret this stack trace as evidence of a flaw in your static initialization scheme for IOS

error is : java.lang.NullPointerException: null
[null] 0:0:0,0 - Exception: java.lang.NullPointerException - null
java.lang.NullPointerException
    at online_shared_Session_Mode.values:97
    at online_shared_commonLobby_2.__CLINIT__:223
    at online_shared_commonLobby.handleDeferredMouse:124
    at online_shared_commonLobby.run:51
    at java_lang_Thread.runImpl:153


Session.Mode is an enum with a custom constructor, but one that can't generate an exception.

There ought to be no way Session.Mode.values() can throw a nullpointerexception.


Dave Dyer

unread,
Jul 1, 2016, 1:58:43 PM7/1/16
to CodenameOne Discussions
It may be relevant, it's possible this use if Session.Mode.values() might be occurring
when no instances of Session have been created yet.

Shai Almog

unread,
Jul 2, 2016, 12:48:04 AM7/2/16
to CodenameOne Discussions
Calling or triggering values() from the constructor of the enum might be legal in Java but I'm not sure if it's something we should support.
I would wonder what JavaSE returns as the values during the construction in such a case... I'm guessing this doesn't work in SE either but fails quietly (e.g. partial set of values).

Dave Dyer

unread,
Jul 2, 2016, 4:03:54 AM7/2/16
to CodenameOne Discussions
The constructor for enum Mode is not calling .values.

There are two classes at issue here, "Session" is an ordinary class, possibly one for which
no instances have been made.  Enum Mode is a component of session.  Session.Mode.values()
is called from an unrelated class.  The constructor for Mode has apparently not been invoked
at the time Mode.values() is called.  Presumably the initialization for Mode would normally
happened as part of the initialization for Session.

Dave Dyer

unread,
Jul 2, 2016, 5:36:21 PM7/2/16
to CodenameOne Discussions

Cutting out  a test case that just does what's apparent in the backtrace doesn't fail,
and for that matter, this failure is a one-of - the whole process involved in launching
the app works in all but this one known instance..  So perhaps there is something
more subtle going on, involving the sequencing of running static initializers and starting
the program threads.


Dave Dyer

unread,
Jul 30, 2016, 3:12:06 AM7/30/16
to CodenameOne Discussions

This is happening non-deterministically but fairly regularly.  I can convince myself
that it's plausible that Session.Mode.values() is being called before any calls to
new Session().  The method commonLobby_2.__CLINIT__ is not one of mine.


Dave Dyer

unread,
Jul 30, 2016, 2:02:40 PM7/30/16
to CodenameOne Discussions

Also, since it's non-deterministic, my working assumption is that the init code
is being entered by two threads, which is somehow resulting in a damaged class
and triggering the problem.

Dave Dyer

unread,
Jul 30, 2016, 5:15:11 PM7/30/16
to CodenameOne Discussions

Speculating a bit above my expertise level here, but it appears there is a window in the
class initialization that could explain this.  Here's part of the initializer for my Session.Mode enum

Reading this, Session_Mode.initialized is set to TRUE, and then online_shared_Session_Mode___CLINIT____
is called.  online_shared_Session_Mode___CLINIT____ is where the enum .values array is initialized.


void __STATIC_INITIALIZER_online_shared_Session_Mode(CODENAME_ONE_THREAD_STATE) {
    if(class__online_shared_Session_Mode.initialized) return;

    class_array1__online_shared_Session_Mode.vtable = initVtableForInterface();
    monitorEnter(threadStateData, (JAVA_OBJECT)&class__online_shared_Session_Mode);
    if(class__online_shared_Session_Mode.initialized) {
        monitorExit(threadStateData, (JAVA_OBJECT)&class__online_shared_Session_Mode);
        return;
    }

    class__online_shared_Session_Mode.vtable = malloc(sizeof(void*) *20);
    __INIT_VTABLE_online_shared_Session_Mode(threadStateData, class__online_shared_Session_Mode.vtable);
    class__online_shared_Session_Mode.initialized = JAVA_TRUE;
    monitorExit(threadStateData, (JAVA_OBJECT)&class__online_shared_Session_Mode);
    online_shared_Session_Mode___CLINIT____(threadStateData);
}

Here's the enum definition

    static enum Mode
    {
        Game(true,"","Game Room","Anyone can play in this room"),
        Chat(false,"x","Chat Room",""),
        Review(false,"x","Review Games","replay or review saved games"),
        Map(false,"x","Map of Player Locations",""),
        Doubles(true,"D!","Doubles Room","Teams of two players can play"),
        Master(true,"M!","Master Room", "Master ranked players can play"),
        Challenge(true,"C!","Challenge Room","The Beat-the-Robot challenge"),
        Unranked(true,"U!","Unranked Room","Games do not affect your Ranking"),
        Table(true,"T!","Table Mode Room","Like playing with real tiles"),
        Cafe(true,"C!","Cafe Room","Games with win/loss scoring and doubling");
       
        static Mode order[] = {Mode.Game,Mode.Cafe,Mode.Doubles,Mode.Master,Mode.Challenge,Mode.Review,Mode.Chat,Mode.Map,Mode.Unranked,Mode.Table};
        static Mode[] getMenuOrder() { return(order); }
        String prefix;
        String name;
        String subhead;
        boolean isAGameMode;
        String getName() { return(name); }
        String getPrefix() { return(prefix); }
        String getSubhead() { return(subhead); }
       
        Mode(boolean g,String p,String n,String s)
        {    isAGameMode = g;
            prefix = p;
            name = n;
            subhead = s;
        }
        boolean isAgameMode() { return(isAGameMode); }
        static Mode getMode (int m) { for(Mode v : values()) { if(v.ordinal()==m) { return(v); }} ; return(null); }
        int getValue() { return(ordinal()); }

    }

online_shared_Session_Mode.m

Shai Almog

unread,
Jul 31, 2016, 12:35:05 AM7/31/16
to CodenameOne Discussions
I see this in the values method call so I'm not sure what you are talking about. This is initialized lazily.

This is the code I'm seeing for a different enum:
JAVA_OBJECT __VALUE_OF_com_codename1_charts_compat_Paint_Style(CODENAME_ONE_THREAD_STATE, JAVA_OBJECT value) {
        JAVA_ARRAY values
= (JAVA_ARRAY)get_static_com_codename1_charts_compat_Paint_Style__VALUES(threadStateData);
    JAVA_ARRAY_OBJECT
* data = (JAVA_ARRAY_OBJECT*)values->data;
   
int len = values->length;
   
for (int i=0; i<len; i++) {
        JAVA_OBJECT name
= get_field_com_codename1_charts_compat_Paint_Style_name(data[i]);
       
if (name != JAVA_NULL && java_lang_String_equals___java_lang_Object_R_boolean(threadStateData, name, value)) { return data[i];}
   
}
   
return JAVA_NULL;
}




It might need synchronization.

Dave Dyer

unread,
Jul 31, 2016, 2:06:34 AM7/31/16
to CodenameOne Discussions
You need a little more external code to see where Mode.values is being accessed, but my point
is that in the static intializer, "intialized" is set to true before the values array is intialized.  If
thread "A" is interrupted at that point, and thread "B", initializing another class, calls the initializer,
it will find the values array is still null.  I think that is the situation in
online_shared_commonLobby_2.__CLINIT__ which is visible in the
stack trace, and which contains this:

label_L810336569:
    PUSH_OBJ(online_shared_Session_Mode_values___R_online_shared_Session_Mode_1ARRAY(threadStateData));
    { /* ARRAYLENGTH */
        if(SP[-1].data.o == JAVA_NULL) {
            throwException(threadStateData, __NEW_INSTANCE_java_lang_NullPointerException(threadStateData));
        };
        SP[-1].type = CN1_TYPE_INT;
        SP[-1].data.i = (*((JAVA_ARRAY)SP[-1].data.o)).length;
    }
 
this appears to be where the nullpointerexception is thrown.

online_shared_commonLobby_2.m

Dave Dyer

unread,
Jul 31, 2016, 3:38:18 PM7/31/16
to CodenameOne Discussions
One more point you may have missed - 100% of the code under discussion is in automatically generated static
initializers.   The proximate offending function causing the NullPointerException, Mode.values() ,is not even referenced
in the sources.

Shai Almog

unread,
Aug 1, 2016, 12:00:58 AM8/1/16
to CodenameOne Discussions
I didn't miss that point. I still don't see what you are referring to. values are initialized lazily as far as I can tell.

Dave Dyer

unread,
Aug 1, 2016, 2:02:25 AM8/1/16
to CodenameOne Discussions

All right, just go back to the basic fact.  IOS automatically generated static initializer is generating a NullPointerException,
some of the time.  Find your own explanation, but please fix it.

Dave Dyer

unread,
Aug 1, 2016, 3:48:33 PM8/1/16
to CodenameOne Discussions
Background processing at 4am gave me an idea how to produce a repeatable
test. I opened issue https://github.com/codenameone/CodenameOne/issues/1843 with a test program.

Shai Almog

unread,
Aug 2, 2016, 12:46:39 AM8/2/16
to CodenameOne Discussions
We'll look into it.

Dave Dyer

unread,
Aug 4, 2016, 6:21:13 PM8/4/16
to CodenameOne Discussions

Thanks for the fix.  As usual, once you have a clear test case the fix was almost immediate.

Reply all
Reply to author
Forward
0 new messages