codenameone / CodenameOne

Cross-platform framework for building truly native mobile apps with Java or Kotlin. Write Once Run Anywhere support for iOS, Android, Desktop & Web.
https://www.codenameone.com/
Other
1.7k stars 402 forks source link

Scrolling freezes app after pause - resume (iOS) #1571

Open jaanushansen opened 9 years ago

jaanushansen commented 9 years ago

Hi!

I run into an annoying bug on iOS:

When app is paused while scrolling is in progress then sometimes after resume the app is totally freezed (not responding to any dragging at all). Unfortunately the bug is not easy to reproduce, but I managed to reproduce it quite often like this:

1) open this app on iOS device:

import com.codename1.io.Log;
import com.codename1.ui.events.*;
import com.codename1.ui.layouts.*;
import com.codename1.ui.*;

import userclasses.StateMachine;

public class ScrollingBug {

    private Form current;

    public void init(Object context) {
    }

    public void start() {
        if(current != null){
            current.show();
            return;
        }
        Form form = new Form();
        form.setLayout(new BorderLayout());
        form.addComponent(BorderLayout.NORTH, new Label("Header"));
        Container panel = new Container();
        panel.setLayout(new BoxLayout(BoxLayout.Y_AXIS));
        List<String> list = new List<String>();
        for (int i = 0; i < 400; i++) {
            list.addItem("item" + i);
        }
        panel.addComponent(list);  
        form.addComponent(BorderLayout.CENTER, panel);
        form.show();
    }

    public void stop() {
        current = Display.getInstance().getCurrent();
    }

    public void destroy() {
    }
}

2) Drag quickly from up to down to start list scrolling motion animation.

3) Click device's lock button to pause the app

4) Unlock your device and resume

5) Try to scroll some more. Now sometimes app just freezes :(

Here is the same scenario on video: https://www.youtube.com/watch?v=8ctuhA4hZeE

jaanushansen commented 8 years ago

Hi!

I wonder, if there is some hope that that you would start to deal with the bug as it is reported by a free user? Or should I rather stop waiting and dig in to the code by myself?

In the report I forgot to mention, that the error is easier to reproduce on older (slower) devices. I was testing on iPhone 4.

And when I did a lot of scrolling, then the same test case produced also an Exception:

photo

Oct 29 11:40:47 BorderTest[1257] : java.lang.ArrayIndexOutOfBoundsException at com_codename1_ui_Display.handleEvent:2010 at com_codename1_ui_Display.edtLoopImpl:1063 at com_codename1_ui_Display.mainEDTLoop:994 at com_codename1_ui_RunnableWrapper.run:120 at com_codename1_impl_CodenameOneThread.run:176 at java_lang_Thread.runImpl:153

saeder commented 8 years ago

A similar bug #1190 has never been fixed and has just been closed by C1. That seems to be the way they handle things. I've given up on that one.

jaanushansen commented 8 years ago

Maybe it helps to solve the originally reported freeze bug. I managed to freeze also on iOS simulator, so I can give the trace of the probably deadlocked Thread (actually I was not running the same sample project, but probably it doesn't give too much difference) :

#0  0x000000010f56148a in __semwait_signal ()
#2  0x000000010f2f39c0 in usleep ()
#3  0x00000001070afe3e in codenameOneGcMalloc at /Volumes/DevDisc/MyApp-src/cn1_globals.m:689
#4  0x0000000108cfef5a in __NEW_java_lang_System_1 at /Volumes/DevDisc/MyApp-src/java_lang_System_1.m:58
#5  0x0000000108cfbe60 in java_lang_System_startGCThread__ at /Volumes/DevDisc/MyApp-src/java_lang_System.m:128
#6  0x0000000108cfcdea in java_lang_System_gc__ at /Volumes/DevDisc/MyApp-src/java_lang_System.m:220
#7  0x00000001070afdf2 in codenameOneGcMalloc at /Volumes/DevDisc/MyApp-src/cn1_globals.m:686
#8  0x00000001070aff97 in allocArray at /Volumes/DevDisc/MyApp-src/cn1_globals.m:776
#9  0x0000000108cce8f2 in java_lang_String___INIT_____char_1ARRAY_int_int at /Volumes/DevDisc/MyApp-src/java_lang_String.m:356
#10 0x0000000108cdf5bc in java_lang_String_substring___int_int_R_java_lang_String at /Volumes/DevDisc/MyApp-src/java_lang_String.m:2299
#11 0x0000000108b0cabe in com_my_framework_ui_UIUtil_getDrawnTextLines___java_lang_String_com_codename1_ui_Font_int_int_R_java_util_Vector at /Volumes/DevDisc/MyApp-src/com_my_framework_ui_UIUtil.m:2547
#12 0x0000000108b0ab66 in com_my_framework_ui_UIUtil_drawTextLines___int_com_codename1_ui_Graphics_java_lang_String_com_codename1_ui_Font_int_int_int_int_int_int_double_R_int at /Volumes/DevDisc/MyApp-src/com_my_framework_ui_UIUtil.m:2325
#13 0x0000000108b0a771 in com_my_framework_ui_UIUtil_drawTextLines___int_com_codename1_ui_Graphics_java_lang_String_com_codename1_ui_Font_int_int_int_int_boolean_double_R_int at /Volumes/DevDisc/MyApp-src/com_my_framework_ui_UIUtil.m:2283
#14 0x0000000108b09fec in com_my_framework_ui_UIUtil_drawTextLines___com_codename1_ui_Graphics_java_lang_String_com_codename1_ui_Font_int_int_int_int_boolean_R_int at /Volumes/DevDisc/MyApp-src/com_my_framework_ui_UIUtil.m:2220
#15 0x0000000107e25c97 in my_comp_apps_logic_history_HistoryLineRenderer_paint___com_codename1_ui_Graphics at /Volumes/DevDisc/MyApp-src/my_comp_apps_logic_history_HistoryLineRenderer.m:1277
#16 0x0000000107477ca2 in virtual_com_codename1_ui_Component_paint___com_codename1_ui_Graphics at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:12610
#17 0x00000001077a18ee in com_codename1_ui_List_renderComponent___com_codename1_ui_Graphics_com_codename1_ui_Component_int_int_int_int at /Volumes/DevDisc/MyApp-src/com_codename1_ui_List.m:5602
#18 0x000000010779679d in com_codename1_ui_List_paint___com_codename1_ui_Graphics at /Volumes/DevDisc/MyApp-src/com_codename1_ui_List.m:5193
#19 0x0000000107477ca2 in virtual_com_codename1_ui_Component_paint___com_codename1_ui_Graphics at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:12610
#20 0x000000010747644f in com_codename1_ui_Component_internalPaintImpl___com_codename1_ui_Graphics_boolean at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:2967
#21 0x000000010747534b in virtual_com_codename1_ui_Component_internalPaintImpl___com_codename1_ui_Graphics_boolean at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:13098
#22 0x0000000107474f1f in com_codename1_ui_Component_paintInternalImpl___com_codename1_ui_Graphics_boolean at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:2831
#23 0x0000000107473cf3 in com_codename1_ui_Component_paintInternal___com_codename1_ui_Graphics_boolean at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:2721
#24 0x000000010747222b in virtual_com_codename1_ui_Component_paintInternal___com_codename1_ui_Graphics_boolean at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:13090
#25 0x00000001075097df in com_codename1_ui_Container_paint___com_codename1_ui_Graphics at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Container.m:3828
#26 0x0000000107477ca2 in virtual_com_codename1_ui_Component_paint___com_codename1_ui_Graphics at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:12610
#27 0x000000010747644f in com_codename1_ui_Component_internalPaintImpl___com_codename1_ui_Graphics_boolean at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:2967
#28 0x000000010747534b in virtual_com_codename1_ui_Component_internalPaintImpl___com_codename1_ui_Graphics_boolean at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:13098
#29 0x0000000107474f1f in com_codename1_ui_Component_paintInternalImpl___com_codename1_ui_Graphics_boolean at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:2831
#30 0x0000000107473cf3 in com_codename1_ui_Component_paintInternal___com_codename1_ui_Graphics_boolean at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:2721
#31 0x000000010747222b in virtual_com_codename1_ui_Component_paintInternal___com_codename1_ui_Graphics_boolean at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:13090
#32 0x00000001075097df in com_codename1_ui_Container_paint___com_codename1_ui_Graphics at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Container.m:3828
#33 0x0000000107477ca2 in virtual_com_codename1_ui_Component_paint___com_codename1_ui_Graphics at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:12610
#34 0x000000010747644f in com_codename1_ui_Component_internalPaintImpl___com_codename1_ui_Graphics_boolean at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:2967
#35 0x000000010747534b in virtual_com_codename1_ui_Component_internalPaintImpl___com_codename1_ui_Graphics_boolean at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:13098
#36 0x0000000107474f1f in com_codename1_ui_Component_paintInternalImpl___com_codename1_ui_Graphics_boolean at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:2831
#37 0x0000000107473cf3 in com_codename1_ui_Component_paintInternal___com_codename1_ui_Graphics_boolean at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:2721
#38 0x000000010747222b in virtual_com_codename1_ui_Component_paintInternal___com_codename1_ui_Graphics_boolean at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:13090
#39 0x00000001075097df in com_codename1_ui_Container_paint___com_codename1_ui_Graphics at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Container.m:3828
#40 0x0000000107477ca2 in virtual_com_codename1_ui_Component_paint___com_codename1_ui_Graphics at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:12610
#41 0x00000001074760a9 in com_codename1_ui_Component_internalPaintImpl___com_codename1_ui_Graphics_boolean at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:2941
#42 0x000000010747534b in virtual_com_codename1_ui_Component_internalPaintImpl___com_codename1_ui_Graphics_boolean at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:13098
#43 0x0000000107474f1f in com_codename1_ui_Component_paintInternalImpl___com_codename1_ui_Graphics_boolean at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:2831
#44 0x0000000107473cf3 in com_codename1_ui_Component_paintInternal___com_codename1_ui_Graphics_boolean at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:2721
#45 0x000000010747222b in virtual_com_codename1_ui_Component_paintInternal___com_codename1_ui_Graphics_boolean at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:13090
#46 0x0000000107472169 in com_codename1_ui_Component_paintInternal___com_codename1_ui_Graphics at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:2565
#47 0x000000010747e8e5 in virtual_com_codename1_ui_Component_paintInternal___com_codename1_ui_Graphics at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:13082
#48 0x000000010747e322 in com_codename1_ui_Component_paintComponent___com_codename1_ui_Graphics_boolean at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:3651
#49 0x000000010747c19b in virtual_com_codename1_ui_Component_paintComponent___com_codename1_ui_Graphics_boolean at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:13154
#50 0x000000010747c0c6 in com_codename1_ui_Component_paintComponent___com_codename1_ui_Graphics at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:3445
#51 0x00000001074d2055 in virtual_com_codename1_ui_Component_paintComponent___com_codename1_ui_Graphics at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Component.m:13146
#52 0x00000001070e70a1 in com_codename1_impl_CodenameOneImplementation_paintDirty__ at /Volumes/DevDisc/MyApp-src/com_codename1_impl_CodenameOneImplementation.m:1029
#53 0x000000010714352d in virtual_com_codename1_impl_CodenameOneImplementation_paintDirty__ at /Volumes/DevDisc/MyApp-src/com_codename1_impl_CodenameOneImplementation.m:11634
#54 0x000000010759351f in com_codename1_ui_Display_edtLoopImpl__ at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Display.m:2834
#55 0x000000010759d723 in com_codename1_ui_Display_mainEDTLoop__ at /Volumes/DevDisc/MyApp-src/com_codename1_ui_Display.m:2445
#56 0x00000001079d2e23 in com_codename1_ui_RunnableWrapper_run__ at /Volumes/DevDisc/MyApp-src/com_codename1_ui_RunnableWrapper.m:470
#57 0x0000000108cc640f in virtual_java_lang_Runnable_run__ at /Volumes/DevDisc/MyApp-src/java_lang_Runnable.m:63
#58 0x000000010715c065 in com_codename1_impl_CodenameOneThread_run__ at /Volumes/DevDisc/MyApp-src/com_codename1_impl_CodenameOneThread.m:482
#59 0x0000000108cc640f in virtual_java_lang_Runnable_run__ at /Volumes/DevDisc/MyApp-src/java_lang_Runnable.m:63
#60 0x0000000108d02ad8 in java_lang_Thread_runImpl___long at /Volumes/DevDisc/MyApp-src/java_lang_Thread.m:241
#61 0x0000000108eb1324 in threadRunner at /Volumes/DevDisc/MyApp-src/nativeMethods.m:1091
#62 0x000000010f52905a in _pthread_body ()
#64 0x000000010f5263ed in thread_start ()
jaanushansen commented 8 years ago

And to make the trace more readable:

in previous stack the EDT thread stays in endless while loop at cn1_globals.m lines 688 - 690:

                while(threadStateData->threadBlockedByGC || threadStateData->heapAllocationSize > 0) {
                    usleep((JAVA_INT)(1000));
                }
jaanushansen commented 8 years ago

And to be bit more precise: the problem root is in System.startGCThread(), which sometimes after app resume just won't start the gc. It happens when startGCThread is called from a thread which at the same time has run out of available memory for malloc. In that case

gcThreadInstance = new Thread("GC Thread") {

has no hope to continue (https://github.com/codenameone/CodenameOne/blob/master/vm/JavaAPI/src/java/lang/System.java).

jaanushansen commented 8 years ago

And here is a quick hack, which seams to solve the total CG freeze issue, still the solution is not perfect because when app starts to print out ArrayIndexOutOfBoundsExcpetion-s, then app is rather unresponsive.

I replaced startGCThread method in https://github.com/codenameone/CodenameOne/blob/master/vm/JavaAPI/src/java/lang/System.java with this code:

    private static void startGCThread() {
        if(!startedGc) {
            startedGc = true;
            // not ideal but does the job for now, since gc is pretty efficient the cost is very low
            gcThreadInstance = initGcThreadInstance();
            gcThreadInstance.start();
        }
    }

    private static Thread nextGCThreadInstance;

    /**
     * Initialize GC thread.
     * Whenever possible, uses previously created Thread instance, 
     * or else we would be in trouble when startGCThread() is called from 
     * a thread, which can't malloc anything before running the gc
     */
    private static Thread initGcThreadInstance() {
        Thread preparedResult = nextGCThreadInstance;
        if (preparedResult != null) {
            //we can use previously created Thread instance
            nextGCThreadInstance = null;
            return preparedResult;
        } 
        else {
            return new Thread("GC Thread") {
                public void run() {
                    //initialize thread for the next call
                    nextGCThreadInstance = initGcThreadInstance();
                    synchronized(LOCK) {
                        // wait two seconds initially so startup won't be hindered by slow waits
                        try {
                            LOCK.wait(2000);
                        } catch (InterruptedException ex) {
                        }
                    }
                    gcShouldLoop = true;
                    while(gcShouldLoop) {
                        try {
                            System.gcMarkSweep();
                            synchronized(LOCK) {
                                if(forceGc || isHighFrequencyGC()) {
                                    forceGc = false;
                                    LOCK.wait(200);
                                } else {
                                    LOCK.wait(30000);
                                }
                            }
                        } catch (InterruptedException ex) {
                        }
                    }
                    startedGc = false;
                    gcThreadInstance = null;
                }
            };
        }
    }