Thread Safety

Discussion in 'Plugin Development' started by Chris Covert, Feb 11, 2011.

Thread Status:
Not open for further replies.
  1. Offline

    Chris Covert

    Hi,

    I'm building a sort of swarm intelligence simulation program capable of building structures in Minecraft as a visualization of the algorithm, and I was wondering exactly what I have to be careful with with regards to thread saftey. Since I have a significant amount of time in which the server needs to be running the algorithm, I decided to make it run on a second thread to avoid disconnecting all connected players; however, in order to update blocks in real time, I need to be able to set block materials from this second thread. Will making a call to

    Code:
    World.GetBlockAt(x,y,z).SetBlockType(Material)
    cause issues with thread safety? If so, is there a way for me to used synchronized code with the server thread? Or perhaps, does anyone know of a better way to fix the problem?

    Any help on this matter would be greatly appreciated.

    Thanks,
    Chris
     
  2. Offline

    eisental

    Yes, use the BukkitScheduler. Put your update code in a Runnable object and run it using getServer().getScheduler().scheduleSyncDelayedTask() with no delay.
     
  3. Offline

    NathanWolf

    Uh... is this implemented now? Really? 'Cause, I have some work to do if so!! :D

    EDIT: Thank you for that link! I didn't even know there were javadocs... :\ And what is that doc viewer?? It's really cool, when can my plugins get their docs integrated into it? ;)
     
  4. Offline

    Byteflux

    Yep it is. It works pretty good, but there's one thing I'm not entirely sure on, which is the server ticks. If you want to try it, just grab the latest bukkit/craftbukkit code from github and build.

    Does anyone know how server ticks are measured? Because I'd like to be able to add repeating tasks with delays based on accurate time measurements.
     
  5. Offline

    eisental

    I can see it needs some free pr work so I'm trying to help :)
    doc viewer is Doxygen. took me a while to get used to but its source integration and search box proved to be extremely useful.
    --- merged: Feb 11, 2011 5:01 PM ---
    1 tick =~ 50ms. 24000 ticks per minecraft day.
     
  6. Offline

    NathanWolf

    Wow- you all ROCK.

    Thanks so much!!

    (And definitely thanks to whatever bukkit dev got that implemented- I want to go find the commit now, just to thank them!)
     
  7. Offline

    Raphfrk

    Server ticks occur roughly 20 times a second. If the server is under a lot of load, then they might occur slower.

    At the moment, real time tasks are not supported. The internal counters in the scheduler are based on server ticks.

    However, you can still use the scheduler to realign them to the main server thread using the 0 delay method.
     
  8. Offline

    Byteflux

    Excellent. Thanks for clarifying.
     
  9. Offline

    Lologarithm

    SO AWESOME!! I have been waiting for this. Next on my todo list: create animated stargate woosh effects on opening :)
     
  10. Offline

    NathanWolf

    Ooooh! I would love to see that in action! :D
     
  11. Offline

    matejdro

    Um, can anyone explains me that one, please?

    I have never worked with a schedulers or something like that. I guess that is some kind of replacement for timers that works inside Bukkit?
     
  12. Offline

    NathanWolf

    That's exactly the idea- since it's not really safe to spawn your own thread to do something at regular intervals, you can ask BukkitScheduler to notify you at regular intervals instead.

    Caveat: I haven't looked closely yet, but I'm guessing this becomes very similar to listening for an event, then- like the old hMod tick event, but customizeable to your needs so you're not wasting tons of cycles.
     
  13. Offline

    matejdro

    Awesome. I will check it out.
     
  14. Offline

    Raphfrk

    Actually, the current implementation isn't event based.

    The original system (WAAAY back :)), was event based.

    You sent an event to the scheduler and the main thread would trigger the event on the next clock tick.

    It wasn't really a scheduler. It was just another method for the pluginManager. There is a callEvent method, it just added a thread safe version of it.

    The scheduler takes Runnables and Callables as inputs (the Callables are for methods that return a value).

    I have written a tutorial for the scheduler on the wiki.
     
  15. Offline

    NathanWolf

    Ah, sorry, Raphfrk- I shouldn't think aloud like that before having actually looked at it- too similar to spreading misinformation :)

    This sounds so awesome- it's going into Persistence and Gameplay very soon- soon as I get a chance, really! Thanks again for putting this together :D
    --- merged: Feb 14, 2011 4:25 PM ---
    Wow... it occurs to me that I didn't even realize there was a whole async side to this. I haven't even started to think about what I could do with that! So, it's safe to go multithreaded as long as you don't call Bukkit API? That makes sense.​
    Well, sheesh, in that case.... I think Persistence incremental auto save can be async! Wow, I need to think on that, but that would be really, really cool. No more getting blamed for server lag ;P​

    --- merged: Feb 14, 2011 4:28 PM ---
    Wow, you've gotten me real worked up about this :) Wish I didn't have a day job.... ;)

    So, I'm thinking I'm going to go ahead and assume I want to do this, unless I get feedback otherwise.

    So far, the only ramifications I can think of is that I'll need to make it very clear that you should never call a Bukkit API function in the getter of a DAO. That's a pretty off thing to be doing anyway, though- I can't think of a single use case where that seems like a good idea.
     
  16. Offline

    Raphfrk

    Well, don't call Bukkit or methods from other plugins which call Bukkit :).​
    Right, you only really need this to link back to the main thread. You can do whatever you want with objects from your own threads.​

    --- merged: Feb 14, 2011 5:31 PM ---
    One thing you would need to watch is syncing between that async thread and your API itself.

    Haven't looked into Persistence, but you would need to make sure that any plugin calling one of your methods is properly synced.

    Also, those methods shouldn't block if at all possible.

    A plugin should be able to call your function and only pause for a momentary sync. You mention in your other post a possible issue with auto-save (assuming I understood correctly).

    - async (write) locks database
    - auto-save begins to write to disk
    - plugin tries to write to database and is locked
    -- main thread is locked
    - <wait a while>
    - auto-save ends
    - plugin write is accomplished

    A better way is to change the write system so that plugin write via a queue

    - plugin writes
    -- write data to queue

    - plugin reads
    -- check if item is in queue
    --- if so, return that item
    --- otherwise, read item from database

    Every so often, you (async) transfer the queue to the database. You could create a "manager" thread to do this.

    This changes the auto-save to
    - async (write) locks database
    - auto-save begins to write to disk
    - plugin tries to write to database
    -- object is written to write queue
    - <wait a while>
    - auto-save ends
    - write queue is completed
     
  17. Offline

    NathanWolf

    Right on- I need to digest all that, but basically I think we're on the same page.

    Right now, mainly just to get things going in a safe way, Persistence is extremely aggressive about locking- basically all access right now is thread-locked.

    So, unless I get that taken care of in a more graceful way, async save is pretty pointless since the main thread would get locked on the first get/put anyway, and I know in my plugins at least I do that quite a bit- NetherGate does at least one get() on each and every player move, for instance.

    A get() is generally just a HashMap lookup, and a put() is often just a lookup and then setting a dirty flag- it's all meant to be very fast so you can just let Persistence manage your instances for you- that's how I use it anyway. For my Persistence-enabled plugins, unless I have to maintain some kind of runtime cache map (like a Chunk->Portal map for NetherGate, for instance) I don't even keep track of my own instances anymore- I just let Persistence manage it for me.

    Anyway, that digression aside, you're completely right- I need to queue up put requests and then access that queue in a thread-safe way, I think. And then allow concurrent reading (concurrent with saving, anyway- locked if there is a put() going on, but put() is fast).

    Needs a lot of thought, but I'm extremely excited about the possibilities.
     
  18. Offline

    Raphfrk

    One point is that in java locks don't actually have to be the objects themselves. You can create a fake object to lock.

    In fact, you have to do it that way for Integers.

    Code:
    Integer x;
    .
    .
    .
    synchronized(x) {
      x=x+1;
    }
    
    The above code is wrong.

    x = x+1;

    really means

    x = new Integer(x.intValue()+1);

    You can create an object to do the lock

    Code:
    
    int x;
    Object intLock = new Object();
    
    synchronized(intLock) {
        x++;
    }
    
    Actually, that has little to do with read/write locks though :), but is a potential gotcha. Originally, I assumed that one of the points of the Integer object instead of int was so that you could use they as syncing elements.

    There is a ReentrantReadWriteLock. However, that isn't as simple to use as synchronized blocks.
     
  19. Offline

    NathanWolf

    Right now, Persistence only uses block synchronization, and has two lock objects - one read and one write.

    But, I'm really not using them properly at the moment, and as I said it's way over-aggressive. For now, better safe than sorry is my thinking.
     
  20. Offline

    Lologarithm

    Definitely take a look at the ConcurrentHashMap (if you are using hashes) - it does lots of cool locking per hash bucket rather than at the object level. Most reads have no locking.

    It seems like you are already doing it but always have a different object as the lock than the object you are editing ;)
     
  21. Offline

    NathanWolf

    Yeah- definitely, Persistence just has two static dummy Object instances (read/write) it uses for locking. For now, anyway- this needs to get smarter, more complex, or maybe even simpler thanks to using some better underlying thread-safe common classes... which of course leads me nicely into...


    I definitely want to look into ConcurrentHashMap - I've been meaning to. Is there a ConcurrentArrayList or some equivalent, too? (sorry, could google/javadoc it, just making conversation, really...)

    I'm hoping it'll save me a lot of time and effort, provided I can wrap my head around it- for instance, I've got two different cache maps to hold instances for each class, so I need to make sure they stay in sync/etc.

    Thanks for the tips! Performant multi-threaded access is definitely something I want Persistence to handle- you should still be able to get at all your data from any thread without stalling anything*, even during a background auto-save. That's my ultimate goal, anyway.

    * Persistence nitty-gritty, getting really off-topic here, sorry- technically, instances are loaded (all at once) on-demand. So, the first time you use a class, there could be a hitch loading all that data.
    I'm not entirely sure how to get around this, I don't want get() to require a callback, for instance- that would seriously complexify using Persistence. I can't really pre-load everything because I don't really know about anything until someone gives me a Class. I could easily add an API function to pre-load data for a class, but the onus would be on the dev to actually call it on startup.

    It's probably not a big deal for most uses cases, NetherGate (for instance) has all its data pretty much loaded by the time the first connected player has taken a few steps- but for some people, this may be a concern.

    So, there is that- but I'm just thinking out loud here, really.
     
  22. Offline

    Lologarithm

    There is a whole line of Concurrent* lists that were included in java.nio - you can google for all the relevant info. There are also tons of perf tests showing just how much faster they are. ConcurrentHashMap when using 1 thread is slower than HashMap, but for 2 or more threads writing it is exponentially (i think) faster than hashmap and locking.

    Off the top of my head
    1. have your class write some kind of list of all the stuff people have written. Then onEnable you read in the list and pre-load everything.
    2. Adding the pre-load function for other devs to use sounds easy and good. If people complain about perf tell them to just use the handy-dandy function you provided to pre-load.
     
  23. Offline

    NathanWolf

    This sounds like something I should be using, right away- I'm going to add an issue for it in my tracker so I keep it in mind. Thanks for all the great info! I've never really had reason to use java.nio before, but it sounds like I definitely do now.

    I'm starting to like #2- it fits in with my overall mentality of "automate as much as possible, provide an easy manual way for everything else". I'm handling data migration using a similar system.

    So, I think I'll go with that for now- especially since it really shouldn't be an issue for the vast majority of plugins, at least in terms of persistence. Unless you restart your server constantly, all the data's gonna be there pretty soon after startup anyway.

    Now, if Persistence ends up also being some sort of "real" db storage and query mechanism, as in for scale- something like BB, where I need to start handling loading and saving large chunks of data randomly on-demand, this will be much more of a concern- but that sort of thing should probably be handled in an async way anyway.
     
  24. Offline

    matejdro

  25. Offline

    darknesschaos

    @matejdro - exactly. The scheduler works off of ticks, not time in ms. 20 ticks is usually a second, but not always and could be delayed a little.
     
  26. Offline

    matejdro

    Well, i guess that delays are not that big, right? Or are they (time/day is slow due to lag on some servers)?
     
  27. Offline

    darknesschaos

    Depends on hardware/#of users/plugins/ect but delays aren't usually that large. the day/night issue is because of this from what I read.
     
  28. Offline

    matejdro

    Hm, i don't want people be jailed for 10 minutes instead of 1, so i think i will wait with transition until this is resolved :)
     
  29. Offline

    darknesschaos

    have it run every few ticks and check the time difference, if it meets the condition run the code, if not, reschedule.
     
  30. Offline

    Raphfrk

    The original scheduler was real time, but then it was changed.

    Even if a real time API calls were added, it would still have to align to ticks as that it when the "heart beat" is sent to the scheduler.

    Another option is to use the scheduler to start another thread and have that thread sleep for 10 mins. When it wakes, it can send a zero delay task to the scheduler.
     
Thread Status:
Not open for further replies.

Share This Page