Является ли Eclipse $ SWITCH_TABLE $ поточно-ориентированным? - PullRequest
3 голосов
/ 22 октября 2019

Появляется, когда Eclipse компилирует перечисление, значения которого используются в операторе switch, добавляет в файл класса перечисления следующее:

  • синтетическое поле: private static int[] $SWITCH_TABLE$mypackage$MyEnum;
  • синтетический метод: static int[] $SWITCH_TABLE$mypackage$MyEnum()
    Лениво инициализирует поле $SWITCH_TABLE$mypackage$MyEnum соответствующими порядковыми значениями перечисления.

И в каждом месте, где происходит переключение значений перечисления, вызывается $SWITCH_TABLE$mypackage$MyEnum()[e.ordinal()].

Исходный код:

package mypackage;

public enum MyEnum {
    FIRST;

    public int get() {
        switch (this) {
            case FIRST:
                return 2;
            default:
                return -1;
        }
    }
}

Декомпилированный код:

package mypackage;

public enum MyEnum {
   FIRST;

   // $FF: synthetic field
   private static int[] $SWITCH_TABLE$mypackage$MyEnum;

   public int get() {
      switch($SWITCH_TABLE$mypackage$MyEnum()[this.ordinal()]) {
      case 1:
         return 2;
      default:
         return -1;
      }
   }

   // $FF: synthetic method
   static int[] $SWITCH_TABLE$mypackage$MyEnum() {
      int[] var10000 = $SWITCH_TABLE$mypackage$MyEnum;
      if (var10000 != null) {
         return var10000;
      } else {
         int[] var0 = new int[values().length];

         try {
            var0[FIRST.ordinal()] = 1;
         } catch (NoSuchFieldError var1) {
         }

         $SWITCH_TABLE$mypackage$MyEnum = var0;
         return var0;
      }
   }
}

Однако, как вы можете видеть здесь, ни поле $SWITCH_TABLE$mypackage$MyEnum не является volatile, ни $SWITCH_TABLE$mypackage$MyEnum() выполняет любую синхронизацию.

Вопрос теперь: действительно ли это потокобезопасно? Если я правильно понимаю модель памяти Java, нет гарантии, что если один поток инициализирует таблицу переключателей (посредством вызова get()), второй поток увидит обновленное значение таблицы переключателей. Вместо этого он может увидеть устаревшее значение, которое не имеет правильных элементов.

Дополнительный вопрос: поскольку нет отношений случай-до , будет ли JVM также разрешено переупорядочивать присвоение$SWITCH_TABLE$ перед элементом изменяется? Если бы это было возможно, могли бы быть условия гонки, когда один поток видит массив, в то время как другой поток все еще модифицирует элементы.

1 Ответ

0 голосов
/ 23 октября 2019

Я запускаю некоторые тесты с jcstress в Windows 7 / OpenJDK 13. Локальные переменные важны!

Этот код должен имитировать опубликованный код, запущенный из 3 потоков, несколько раз:

// concurrency stress test
@JCStressTest

// expected outcome (III_Result::toString)
@Outcome(id = "1, 1, 1", expect = Expect.ACCEPTABLE, desc = "Correctly initialized.")
@Outcome(id = ".+, .+, .+", expect = Expect.FORBIDDEN, desc = "Some Thread did see a not initialized array")

// test data
@State
public class EnumInitOK {

    int[] array;

    int[] switchTable() {
        int[] v1 = array;
        if (v1 != null) {
            return v1;
        } else {
            int[] v2 = new int[1];
            v2[0] = 1;
            array = v2;
            return v2;
        }
    }

    // each called only by one particular Thread, once per State instance
    @Actor public void test1(III_Result r) {
        r.r1 = switchTable()[0];
    }

    @Actor public void test2(III_Result r) {
        r.r2 = switchTable()[0];
    }

    @Actor public void test3(III_Result r) {
        r.r3 = switchTable()[0];
    }
}

Результаты всех запусков с разными настройками, без одной ошибки инициализации (почти 400M тестов):

Java Concurrency Stress Tests
---------------------------------------------------------------------------------
Rev: null, built by cfh with 12.0.2 at null

Probing what VM modes are available:
 (failures are non-fatal, but may miss some interesting cases)

----- [OK] [-Xint]
----- [OK] [-XX:TieredStopAtLevel=1]
----- [OK] []
----- [OK] [-XX:+UnlockDiagnosticVMOptions, -XX:+StressLCM, -XX:+StressGCM]
----- [OK] [-XX:-TieredCompilation]
----- [OK] [-XX:-TieredCompilation, -XX:+UnlockDiagnosticVMOptions, -XX:+StressLCM, -XX:+StressGCM]

Initializing and probing the target VM: 
 (all failures are non-fatal, but may affect testing accuracy)

----- [OK] Unlocking diagnostic VM options
Burning up to figure out the exact CPU count....... done!

----- [OK] Trimming down the default VM heap size to 1/4-th of max RAM
----- [OK] Trimming down the number of compiler threads
----- [OK] Trimming down the number of parallel GC threads
----- [OK] Trimming down the number of concurrent GC threads
----- [OK] Trimming down the number of G1 concurrent refinement GC threads
----- [OK] Testing @Contended works on all results and infra objects
----- [OK] Unlocking Whitebox API for online de-optimization
----- [OK] Testing allocation profiling
----- [OK] Trying Thread.onSpinWait

  Hardware threads in use/available: 4/4, using Thread.onSpinWait()
  Test preset mode: "default"
  Writing the test results to "jcstress-results-2019-10-22-23-32-40.bin.gz"
  Parsing results to "results/"
  Running each test matching "cfh.jcstress.EnumInitOK" for 1 forks, 5 iterations, 1000 ms each
  Each JVM would execute at most 5 tests in the row.
  Solo stride size will be autobalanced within [10, 10000] elements, but taking no more than 100 Mb.

... [output deleted] ...

RUN COMPLETE.

RUN RESULTS:
------------------------------------------------------------------------------------------------------------------------

*** INTERESTING tests
  Some interesting behaviors observed. This is for the plain curiosity.

  0 matching test results. 

*** FAILED tests
  Strong asserts were violated. Correct implementations should have no assert failures here.

  0 matching test results. 

*** ERROR tests
  Tests break for some reason, other than failing the assert. Correct implementations should have none.

  0 matching test results. 

*** All remaining tests
  Tests that do not fall into any of the previous categories.

  6 matching test results. 
      [OK] cfh.jcstress.EnumInitOK
    (JVM args: [-XX:+UnlockDiagnosticVMOptions, -XX:+StressLCM, -XX:+StressGCM])
  Observed state   Occurrences   Expectation  Interpretation                                              
      .+, .+, .+             0     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 1    76.194.931    ACCEPTABLE  Correctly initialized.                                      

      [OK] cfh.jcstress.EnumInitOK
    (JVM args: [-XX:-TieredCompilation, -XX:+UnlockDiagnosticVMOptions, -XX:+StressLCM, -XX:+StressGCM])
  Observed state   Occurrences   Expectation  Interpretation                                              
      .+, .+, .+             0     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 1    76.756.011    ACCEPTABLE  Correctly initialized.                                      

      [OK] cfh.jcstress.EnumInitOK
    (JVM args: [-XX:-TieredCompilation])
  Observed state   Occurrences   Expectation  Interpretation                                              
      .+, .+, .+             0     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 1    78.453.381    ACCEPTABLE  Correctly initialized.                                      

      [OK] cfh.jcstress.EnumInitOK
    (JVM args: [-XX:TieredStopAtLevel=1])
  Observed state   Occurrences   Expectation  Interpretation                                              
      .+, .+, .+             0     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 1    73.608.821    ACCEPTABLE  Correctly initialized.                                      

      [OK] cfh.jcstress.EnumInitOK
    (JVM args: [-Xint])
  Observed state   Occurrences   Expectation  Interpretation                                              
      .+, .+, .+             0     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 1     8.740.641    ACCEPTABLE  Correctly initialized.                                      

      [OK] cfh.jcstress.EnumInitOK
    (JVM args: [])
  Observed state   Occurrences   Expectation  Interpretation                                              
      .+, .+, .+             0     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 1    86.069.111    ACCEPTABLE  Correctly initialized.                                      


------------------------------------------------------------------------------------------------------------------------

Для перекрестной проверки я пробовалтот же код, обращающийся к array напрямую, без использования локальных переменных, в методе switchTable:

//concurrency stress test
@JCStressTest

//expected outcome (III_Result::toString)
@Outcome(id = "1, 1, 1", expect = Expect.ACCEPTABLE, desc = "Correctly initialized.")
@Outcome(id = ".+, .+, .+", expect = Expect.FORBIDDEN, desc = "Some Thread did see a not initialized array")

//test data
@State
public class EnumInitErr {

    int[] array;

    int[] switchTable() {
        if (array != null) {
            return array;
        } else {
            array = new int[1];
            array[0] = 1;
            return array;
        }
    }

    // each called only by one particular Thread, once per State instance
    @Actor public void test1(III_Result r) {
        r.r1 = switchTable()[0];
    }

    @Actor public void test2(III_Result r) {
        r.r2 = switchTable()[0];
    }

    @Actor public void test3(III_Result r) {
        r.r3 = switchTable()[0];
    }
}

На этот раз мы получили некоторые результаты с неинициализированными массивами:

... [output deleted] ...

RUN COMPLETE.

RUN RESULTS:
------------------------------------------------------------------------------------------------------------------------

*** INTERESTING tests
  Some interesting behaviors observed. This is for the plain curiosity.

  0 matching test results. 

*** FAILED tests
  Strong asserts were violated. Correct implementations should have no assert failures here.

  6 matching test results. 
  [FAILED] cfh.jcstress.EnumInitErr
    (JVM args: [-XX:+UnlockDiagnosticVMOptions, -XX:+StressLCM, -XX:+StressGCM])
  Observed state   Occurrences   Expectation  Interpretation                                              
         0, 0, 1           565     FORBIDDEN  Some Thread did see a not initialized array                 
         0, 1, 0         1.089     FORBIDDEN  Some Thread did see a not initialized array                 
         0, 1, 1         2.835     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 0, 0           792     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 0, 1         3.272     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 0         2.394     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 1    70.441.694    ACCEPTABLE  Correctly initialized.                                      

  [FAILED] cfh.jcstress.EnumInitErr
    (JVM args: [-XX:-TieredCompilation, -XX:+UnlockDiagnosticVMOptions, -XX:+StressLCM, -XX:+StressGCM])
  Observed state   Occurrences   Expectation  Interpretation                                              
         0, 0, 1           481     FORBIDDEN  Some Thread did see a not initialized array                 
         0, 1, 0           604     FORBIDDEN  Some Thread did see a not initialized array                 
         0, 1, 1         2.373     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 0, 0           901     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 0, 1         2.908     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 0         3.310     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 1    69.456.024    ACCEPTABLE  Correctly initialized.                                      

  [FAILED] cfh.jcstress.EnumInitErr
    (JVM args: [-XX:-TieredCompilation])
  Observed state   Occurrences   Expectation  Interpretation                                              
         0, 0, 1         1.332     FORBIDDEN  Some Thread did see a not initialized array                 
         0, 1, 0           948     FORBIDDEN  Some Thread did see a not initialized array                 
         0, 1, 1         4.212     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 0, 0         1.071     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 0, 1         5.216     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 0         5.052     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 1    64.580.410    ACCEPTABLE  Correctly initialized.                                      

  [FAILED] cfh.jcstress.EnumInitErr
    (JVM args: [-XX:TieredStopAtLevel=1])
  Observed state   Occurrences   Expectation  Interpretation                                              
         0, 0, 1         8.766     FORBIDDEN  Some Thread did see a not initialized array                 
         0, 1, 0        11.470     FORBIDDEN  Some Thread did see a not initialized array                 
         0, 1, 1        21.761     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 0, 0        11.280     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 0, 1        28.461     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 0        27.634     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 1    71.079.569    ACCEPTABLE  Correctly initialized.                                      

  [FAILED] cfh.jcstress.EnumInitErr
    (JVM args: [-Xint])
  Observed state   Occurrences   Expectation  Interpretation                                              
         0, 0, 1            77     FORBIDDEN  Some Thread did see a not initialized array                 
         0, 1, 0           117     FORBIDDEN  Some Thread did see a not initialized array                 
         0, 1, 1         2.417     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 0, 0            83     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 0, 1         2.291     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 0         1.789     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 1     5.674.177    ACCEPTABLE  Correctly initialized.                                      

  [FAILED] cfh.jcstress.EnumInitErr
    (JVM args: [])
  Observed state   Occurrences   Expectation  Interpretation                                              
         0, 0, 1           785     FORBIDDEN  Some Thread did see a not initialized array                 
         0, 1, 0           804     FORBIDDEN  Some Thread did see a not initialized array                 
         0, 1, 1         2.717     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 0, 0         1.250     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 0, 1         4.041     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 0         5.500     FORBIDDEN  Some Thread did see a not initialized array                 
         1, 1, 1    63.510.464    ACCEPTABLE  Correctly initialized.                                      


*** ERROR tests
  Tests break for some reason, other than failing the assert. Correct implementations should have none.

  0 matching test results. 

*** All remaining tests
  Tests that do not fall into any of the previous categories.

  0 matching test results. 

------------------------------------------------------------------------------------------------------------------------

первый раз с использованием jcstress, извините за любые ошибки

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...