java.lang.IllegalAccessError

Discussion in 'Plugin Development' started by escortkeel, Jun 20, 2012.

  1. Offline

    escortkeel

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Hi!

    One of my plugin's users has managed to find a very obscure multi-threading related bug. Multi-threading is required in the plugin because it receives instructions over TCP/IP and must respond to them promptly and accordingly.

    The bug causes the following stack trace to be printed to the console:

    Code:
    [WARNING] Could not properly handle event BLOCK_PHYSICS:
    java.lang.IllegalAccessError: Synchronized code got accessed from another thread: me.escortkeel.remotebukkit.ConnectionHandler
    at org.bukkit.event.Listener.onBlockPhysics(Listener:0)
    at sun.reflect.GeneratedMethodAccessor34.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.bukkit.plugin.java.JavaPluginLoader$1.execute(JavaPluginLoader.java:302)
    at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:62)
    at org.bukkit.plugin.SimplePluginManager.fireEvent(SimplePluginManager.java:477)
    at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:462)
    at net.minecraft.server.World.k(World.java:510)
    at net.minecraft.server.World.applyPhysics(World.java:496)
    at net.minecraft.server.World.update(World.java:459)
    at net.minecraft.server.World.setTypeId(World.java:434)
    at org.bukkit.craftbukkit.block.CraftBlock.setTypeId(CraftBlock.java:92)
    at org.bukkit.craftbukkit.block.CraftBlock.setType(CraftBlock.java:88)
    at com.tommytony.war.volume.Volume.resetBlocks(Volume.java:270)
    at com.tommytony.war.War.unloadWar(War.java:278)
    at com.tommytony.war.War.onDisable(War.java:118)
    at org.bukkit.plugin.java.JavaPlugin.setEnabled(JavaPlugin.java:217)
    at org.bukkit.plugin.java.JavaPluginLoader.disablePlugin(JavaPluginLoader.java:363)
    at org.bukkit.plugin.SimplePluginManager.disablePlugin(SimplePluginManager.java:400)
    at me.ryanclancy000.plugman.PlugManCommands.reloadPlugin(PlugManCommands.java:419)
    at me.ryanclancy000.plugman.PlugMan.doCommand(PlugMan.java:145)
    at me.ryanclancy000.plugman.PlugMan.onCommand(PlugMan.java:34)
    at org.bukkit.command.PluginCommand.execute(PluginCommand.java:40)
    at org.bukkit.command.SimpleCommandMap.dispatch(SimpleCommandMap.java:166)
    at org.bukkit.craftbukkit.CraftServer.dispatchCommand(CraftServer.java:480)
    at me.escortkeel.remotebukkit.ConnectionHandler.run(ConnectionHandler.java:61)
    2012-06-18 17:44:59 [INFO] This error is logged only once: it could have occurred multiple times by now.
    2012-06-18 17:44:59 [INFO] Please contact one of the authors of plugin 'RemoteBukkit': Keeley Hoek (escortkeel)
    While I am aware that this error is thrown by the JVM when compiled code attempts to perform an illegal operation that the compiler would normally catch, I don't understand exactly what, in this instance, is illegal about my thread dispatching a command a console.

    Thanks in advance,
    Keeley :)

    This post has been edited 2 times. It was last edited by escortkeel Jun 20, 2012.
  2. Offline

    Bone008

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    To me, the stack-trace looks like you are trying to dispatch a command from a separate thread, and that command changes a block.

    It's not safe to do that. In fact, almost nothing you can do with the minecraft server / bukkit API is thread-safe.
    What is happening here is you execute the command from the separate thread, which calls some command handler, which sets a block type somewhere, which triggers some block updates, which fire the BlockPhysicsEvent.
    ALL that happens on your different thread. I hope you understand the severity of the issues that rise from that.

    What you need to do to safely access anything useful of the bukkit API (with a couple of exceptions) is create some sort of "operation queuing" system that is synchronized. Your TCP thread receives something that should be done and it appends an executor (Runnable, Callable, whatever you need) to a queue type of thing (for example a LinkedList, make sure you synchronize it properly).
    Then, you have a sync repeating task running (using the BukkitScheduler) every 1 or 2 ticks (maybe even less frequently, if operations don't occur that frequently and it's not a problem that they might be delayed by half a second or so).
    That task polls your operation queue and executes the operations (remember, you are now on the main thread, so you may do that now).

    You probably need to retrieve results in your TCP thread, so I'd use Callables and make your thread block until the operation has been performed. That's also explained on the wiki page by the way.
  3. Offline

    escortkeel

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Thanks so much Bone008!

    You really shed light on the problem. Really appreciate it!. :D

    Thanks,
    Keeley
  4. Offline

    Bone008

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Oh, actually, when I think about it again. My suggestion is complete bullshit :D
    That would be the solution you'd have to take if the scheduler didn't exist (like in any other normal Java program). But that queuing system is actually exactly what the scheduler does for you.

    So, all you have to do is schedule a task or call a sync method with the scheduler, that's wayyy simpler.

    The simplest case: You just want some MC method to be called. The scheduler itself is thread-safe, so you can do (in your custom thread):

    Code:
    Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(new Runnable(){
        @Override
        public void run(){
            someUnsafeBukkitMethod();
        }
    });
    The scheduler will take care of anything else and execute your Runnable at the next server tick.
  5. Offline

    ferrybig

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    I have recently designed an class to make the comunucation between the ukkit thread and its own thread easier by an kind of task dispatch system thingy, like the SwingWorker class
  6. Offline

    escortkeel

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Hahaha :D. Thanks for the new Suggestion! :p

    ~Keeley
  7. Offline

    ferrybig

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    look inside my signature for a other way to combine networking (blocking taskt) whit tasks that invoke bukkit (sending messaging to players, using other world methodes) whitout causing thread problems

Share This Page