Войти или зарегистрироваться
  • Вы находитесь в сообществе Rubukkit. Мы - администраторы серверов Minecraft, разрабатываем собственные плагины и переводим на различные языки плагины наших коллег из других стран.

Помогите ужасный ConcurrentModificationException

DPOH-VAR

Старожил
Пользователь
Баллы
153
Вот такая проблема.
Кто встречал, как с ней бороться?
Код:
java.util.ConcurrentModificationException
    at java.util.AbstractList$Itr.checkForComodification(Unknown Source)
    at java.util.AbstractList$Itr.next(Unknown Source)
    at net.minecraft.server.World.tickEntities(World.java:1149)
    at net.minecraft.server.WorldServer.tickEntities(WorldServer.java:428)
    at net.minecraft.server.MinecraftServer.q(MinecraftServer.java:564)
    at net.minecraft.server.DedicatedServer.q(DedicatedServer.java:213)
    at net.minecraft.server.MinecraftServer.p(MinecraftServer.java:474)
    at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:406)
    at net.minecraft.server.ThreadServerApplication.run(SourceFile:539)
Сгенерированный отчет: ---- Minecraft Crash Report ----
Возникает случайным образом во время выполнения скрипта, при взрыве: Varscript 0.3 (видео)
Причину проследить не удалось, т.к. ошибка выскакивает асинхронно. скрипт продолжает работать.
После ошибки игрок продолжает находиться на сервере, видит все сообщения, но не принимает изменения блоков. Не может писать в чат. Фактически, игрок не меняет своего положения на сервере.
Консоль сервера отвечает на команды, можно сделать reload, но он ничего не меняет.
Новые игроки не могут подключиться.
 
Многие операции проводить асинхронно нельзя.
Какие это операции?
Есть ли список методов, которые нельзя выполнять асинхронно?
пс: у меня почти все методы выполняются в разных потоках.
 
An async thread CAN be used for
  • Scheduler method calls
  • Other tasks which don't access the plugin API
An async thread MUST be used for
  • Tasks which put the current thread to sleep
    • This includes tasks which block, i.e. network read tasks
An async thread MUST NOT be used for
  • Tasks which call Bukkit API methods (except thread safe method, see below)
- wiki.bukkit.org
 
Вы же понимаете, что без исходников вам причину не укажут? Если очень навскидку, то напомню, что любые действия с глобальными списками нужно производить только в блоке synchronized, и я бы на эту тему изучал FreezeBox.java, особенно в окрестностях строк 31 и 71.

Ну и да, если вы итерируетесь по списку, например в цикле for, вы не должны его менять.
 
Вы же понимаете, что без исходников вам причину не укажут? Если очень навскидку, то напомню, что любые действия с глобальными списками нужно производить только в блоке synchronized, и я бы на эту тему изучал FreezeBox.java, особенно в окрестностях строк 31 и 71.

Ну и да, если вы итерируетесь по списку, например в цикле for, вы не должны его менять.
Тут даже не в этом-то дело, сам Bukkit не позволяет работать асинхронно с API.
 
Тут даже не в этом-то дело, сам Bukkit не позволяет работать асинхронно с API.
...если не соблюдать предосторожностей и не иметь карты минного поля. А она у нас имеется. В частности, можно увидеть, что, скорее всего, FreezeBox.java:71 модифицирует список Tile Entity во время их обновления по тику в World.java:1148-1172. Лично я бы попробовал синхронизировать getWorld().tileEntityList и посмотреть, не будет ли падений.
 
пробовал использовать при вызове API synchronized(Bukkit.getServer()){...}
- приводит к взаимной блокировке (причина в структуре плагина)
Сейчас попробую вызывать свой Event с параметром Runnable, отлавливать его и исполнять.
 
пробовал использовать при вызове API synchronized(Bukkit.getServer()){...}
- приводит к взаимной блокировке (причина в структуре плагина)
Сейчас попробую вызывать свой Event с параметром Runnable, отлавливать его и исполнять.
Не надо весь сервер синхронизировать, у вас же падения в одном месте. Или нет?
 
Не надо весь сервер синхронизировать, у вас же падения в одном месте. Или нет?
Все верно. Но я заранее не знаю, по какому миру нужно провести синхронизацию.
Класс FreezeBox используется для паузы в потоке, синхронизированной по объекту. В нем не используется Bukkit API
 
Все верно. Но я заранее не знаю, по какому миру нужно провести синхронизацию.
Класс FreezeBox используется для паузы в потоке, синхронизированной по объекту. В нем не используется Bukkit API
Может покажете код этого класса?
 
Может покажете код этого класса?
Java:
package me.dpohvar.varscript.utils.box;
/**
* Created by IntelliJ IDEA.
* User: DPOH-VAR
* Date: 12.08.12
* Time: 23:09
* To change this template use File | Settings | File Templates.
*/
 
public class FreezeBox<T> {
    Object o = null;
    boolean melted = false;
    public FreezeBox(){}
    public void freeze(final T freezing){
        if (melted) {
            return;
        }
        if (o!=null) throw new RuntimeException("can't freeze at FreezeBox");
        o=freezing;
        synchronized (o){
            try {
                while(needWait()){
                    o.wait();
                }
            } catch (InterruptedException ignored) {
            }
        }
    }
    private boolean needWait(){
        if (melted) return false;
        if (o==null) return false;
        if (!(o instanceof Thread)) return true;
        if (((Thread)o).isInterrupted()) return false;
        return true;
    }
 
    public void freeze(final T freezing,int milliseconds){
        if (melted) return;
        if (o!=null) throw new RuntimeException("can't freeze at FreezeBox");
        o=freezing;
        synchronized (o){
            try {
                if(needWait()) o.wait(milliseconds);
            } catch (InterruptedException ignored) {
            }
        }
    }
    public void unfreeze(){
        melted=true;
        if (o==null) return;
        Object t=o;
        o=null;
        synchronized (t){
            t.notify();
        }
    }
    public void unfreezeAfter(final int ms){
        new Thread(){
            @Override public void run(){
                synchronized (this){
                    try {
                        this.wait(ms);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                unfreeze();
            }
        }.start();
    }
}
Вот основное его действие:

  1. synchronized (o){
  2. try {
  3. if(needWait()) o.wait(milliseconds);
  4. } catch (InterruptedException ignored) {
  5. }
  6. }

    Т.е. обычный wait переданного объекта, но при этом на него не влияют notify, вызванные вне объекта FreezeBox
 
Всем спасибо, синхронизация по Bukkit.getServer() решила проблему.
Вот только не все можно синхронизировать по нему.
Если у кого еще будет похожая проблема, вот решение:
Оборачиваем все вызовы Bukkit API в
Java:
synchronized(Bukkit.getServer()){
    // Bukkit API
}
но нельзя оборачивать вызовы события и любые длительные методы, иначе сервер просто зависнет.
 

Поделиться этой страницей

Назад
Верх