Skip to content

IOS enum static initializers can fail #1843

@ddyer0

Description

@ddyer0

IOS static intiializers can fail if multiple threads call the initializer, because the enum is
marked "initialized" before it actually is initialized. The embedded sample will fail,
indicated by the line "Crash in T2" in the window. Refer to this thread https://groups.google.com/forum/#!topic/codenameone-discussions/zMIMnjaoqAM for the original report and extended discussion. Also note that without the catch Throwable in the main thread, linked to some
kind of active error reporting, the symptom of this kind of failure would be that some thread
appears to just not start. A typical user would shrug, curse, and try again, and probably succeed.

''''Java
package dtest.boardspace;
//
// this version shows the enum initialization problem
//

import com.codename1.ui.Display;
import com.codename1.ui.Form;
import com.codename1.ui.TextArea;
import com.codename1.ui.plaf.UIManager;
import com.codename1.ui.util.Resources;

// collects messages, performs delays
class m
{ static long first =0;
static String message="";
static Thread targetThread = null;
static Thread r1Thread = null;
static Thread r2Thread = null;
static Thread t1Thread = null;
static Thread t2Thread = null;
static Thread WThread = null;
static String getThreadID()
{ Thread tt = Thread.currentThread();

    if(tt==r1Thread) return("R1");
    if(tt==r2Thread) return("R2");
    if(tt==t1Thread) return("T1");
    if(tt==t2Thread) return("T2");
    if(tt==WThread)  return("WW");
    return(tt.toString());

}
static synchronized void add(String m)
{ long now = System.currentTimeMillis();
  if(first==0) { first=now; }
  String tid = getThreadID();
  message += ""+(now-first)+" : "+ tid + " "+m+"\n"; }

public synchronized void delay(int n)
{
    try { wait(n); } catch(InterruptedException e) {}
}
public void busy(int n)
{
    long now = System.currentTimeMillis();
    while(System.currentTimeMillis()-now<n) {}
}

}

// class that might crash while running static initializer
class R1 implements Runnable
{
static int n = ecase.values().length;
public void run()
{ m.r1Thread = Thread.currentThread();
m.add("r1 run");
}
static { m.add("r1 initializer "+n); }
}

// class that might crash while running static initializer
class R2 implements Runnable
{
static int n = ecase.values().length;
public void run()
{ m.r2Thread = Thread.currentThread();
m.add("r2 run");
}
static { m.add("r2 initializer "+n); }
}
// start and run a crashable class
class T1 implements Runnable
{
public void run ()
{
try {
m.t1Thread = Thread.currentThread();
m.add("t1 run");
new Thread( new R1()).start();
}
catch (Throwable err)
{
m.add("Crash in T1 "+err);
}
}
static { m.add("t1 initializer");}
}
// start and run a crashable class
class T2 implements Runnable
{
public void run ()
{ try {
m.t2Thread = Thread.currentThread();
m.add("t2 run");
new Thread(new R2()).start();
}
catch (Throwable err)
{
m.add("Crash in T2 : "+err);
}
}

static { m.add("t2 initializer");}

}
// present the messages
class TWindow extends TextArea implements Runnable
{
public void run()
{ m.WThread = Thread.currentThread();
while(true)
{
setText(m.message);
repaint();
new m().delay(1000);
}
}
}

public class Dtest {
private Form current;
@SuppressWarnings("unused")
private Resources theme;

public void init(Object context) {
    theme = UIManager.initFirstTheme("/theme");
    // Pro only feature, uncomment if you have a pro subscription
    // Log.bindCrashProtection(true);
}
int loops = 0;
TWindow win = null;

public void start() {

    if(current != null){
        current.show();
    }
    new Thread(new T1()).start();
    new m().delay(1000);
    new Thread(new T2()).start();
    current = new Form("Enum initiialization test");
    new m().delay(2000);
    win = new TWindow();
    new Thread(win).start();
    current.add(win);
    win.setVisible(true);
    current.show();


 }


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

public void destroy() {
}

}
enum ecase
{
a(0),
b(1000),
c(2000),
a1(0),
b1(1000),
c1(2000);
int delay;
ecase(int n)
{ delay = n;
m.targetThread = Thread.currentThread();
new m().busy(n);
m.add("new "+this.toString());
};
static
{ m.add("ecase initializer");
m d = new m();
d.delay(1000);
m.add("ecase initialized");
}
}

''''

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions