How do you load things in the background?

@pspeed I need sometimes to build a reference in the main thread because I need the result at once. The reference may have already been passed to the Builder, thought.

The danger is that the reference is built twice, in the two threads. What’s your suggestion for that case ?

I want to try to add one boolean ‘beingBuilt’ and ‘built’ set to true respectively at the beginning and the end of the reference.build() method. Do you think it will be sufficient?

And is there a solution for the (rare) case where the building has began in the Builder thread? Do you see a way to interrupt it?

For your understanding, I need the regions at once when the terrain edition tools are acting on the border of the built region. The tools need to wait for the neighboring regions to be built, or else the terrains won’t be edited in the same way at the border.

If it is already loading and you really need it, you could block your render thread till it is loaded.Also note that you would neeed at least a volatile variable to read it from multiple threads savely

Alternatively whatever solution, what if it loads twice? just before attaching in an enqueued method dispose the duplicate.

Suggestion: don’t do that. Either truly build it twice as in two different builder references or only build it on the builder thread and properly deal with ‘waiting for it to complete’.

I think you will drive yourself crazy otherwise.

The part that gates the state of the reference is kept internal to the Builder itself and you have no access to that. Attempting to lace thread protection inside your builder reference implementation is going to be painful, I think… at best. At worst you will create strange deadlock situations.

Probably easier to prioritize them high, pause/add/resume them and then wait for them to complete. That part is something you could build into your references using standard thread constructs.

There is no way to interrupt it once started.

Just added :

while(regionIsNotBuilt)
    Thread.sleep(5)

Works perfectly. I’ve things to learn about multi-threading… Thanks guys !

Where did you add that?

Is regionIsNotBuilt marked volatile?

There are more thread-friendly ways to do something like that, by the way. Just need more context.

Here are the two methods of my RegionPager in pseudo code.

void setNeededRegion(List<RegionID>){
    // meant to be run in the JME thread at each tick.
    getBuilder().release(obsoleteRegionRefs);
    currentRefs.removeAll(obsoleteRegionRefs)

    getBuilder().build(newRegionRefs);
    currentRefs.addAll(newRegionRefs)
}

Region getRegionAtOnce(RegionID id){
    // meant to be called from another thread (JavaFX)
    if(!currentRefs.contains(id)){
        RegionReference ref = new RegionReference(id);
        synchronized(this){
            currentRefs.add(ref);
            getBuilder.build(ref);
        }
        while(!builtRegions.contains(ref.getRegion())
            // builtRegions receive regions when ref.apply() is called
            // this collection should be concurrent maybe?
            Thread.sleep(5);
    }
}

Do you see possible issues or better way to do this?

It the apply is being done on the same thread then it should be ok… but I don’t know how it could work then because the builtRegions would never get filled. If it’s actually different threads then you’d absolutely need a concurrent data structure of some kind.

But in general, busy waiting is kind of the poor-mans way of doing this. I was about to offer a solution but now I’m confused about the threading you have.

There are three Threads :

  • Jmonkey calls setNeededRegions() each frame (if the camera has moved for a certain distance).
  • JavaFX calls getRegionsAtOnce() when a tool is drawing on the terrain (ie. on mouse drag)
  • Builder does you-know-what

Why does my code work? :slight_smile: I will do more test to check the current thread running everything.

Thanks !

Seems like you’d still be better off just letting the builder thread do its thing and not waiting. Just make sure to give those tiles a higher priority. It’s not like it’s actually going to be any faster waiting and the UI will at least continue to feel responsive.

If it’s that you need to wait for several regions to finish that’s also a solvable problem… but especially asynchronously.

As to why your not-threadsafe data structure works… that’s the way it goes. It might work sometimes or fail catastrophically at others (depending on the structure). (One classic example is a regular HashMap where there is a chance for a get() to run forever if a put() happens at just the right time on another thread.)

For waiting for a single tile, you could use wait/nofity inside the BuilderReference implementaiton in some waitForDone() method or something.

If it were me and I had to watch for a bunch of tiles to be done, I’d probably use a counter and just not update the visual part until the counter (AtomicInteger probably) was a certain value… ie: skip the apply updates until the appropriate number of tiles had been loaded or whatever. Then just make sure to prioritize appropriately. And if it’s a tile that is already waiting, make sure to pause()/resume() around your adds so that they all get resorted properly.

In rendere thread just keep a map of currently loading builders, all data field that determine where it is loading (eg grid 4,5) are final and set in constructor of the key. They are thread safe that way. hashcode and equals to the cords.

in the builder finish part, where you enqueed t the render thread, remove that object from the list.

Now you can do from renderer always a contains(coordinate) on the hashmap to know the if it is loading. (also keep somehwere what is not loaded at all.

In the jfx thread just enque all changes that might cause to jme render thread (one complexity less)

Now the renderer thread can decide if it loads internlal, or dispatches to the background loader.

After a few test, it seems it’s safe to enqueue new references into the Builder from any other thread :

  • build() is always called from Builder thread
  • apply()/release() are always called from the att state thread.

This is certainly a very efficient piece of code :smile:

So to get built and applied references at once, I have to stop whatever thread is enqueuing the job until Builder and BuilderState have finished.

Currently, BuilderState put and remove finished jobs into a concurrent collection (at apply() and release()). The enquing thread sleeps until the asked job is present in the collection. Sleep is certainly ugly, so it goes in the TODO list ^^

This is exactly what I’m doing in RegionID class : immutable string and coord, with hashcode and equals managed. Thanks to strengthen me in my choice :smile:

I can’t see the implementation. I’m not comfortable with the wait()/notify() mechanism but I wil try this someday. Thanks !

The beauty of asynchronous is that you don’t need locks or concurrent queues for the data or anything, you just make sure that the thread that enqueues the calls gets everything in order. So if one thread is assigning the jobs in the correct order its sure that the worker thread will execute them in order, theres no blocking needed. If the main thread needs to know about the job being finished you just enqueue a call from the worker thread.

Ah well the question was for the requirement for imidiate data.

I had a similar solution, in the worst case, blocking the game until the grid has loaded is better, than it falling trough non loaded terrain.

Nowadays I would probably instead set the tpf to 0, so that the gamelogic does not progress any fruther till it is loaded in the background thread, as everything else stays responsible that way.

In this case it’s an editor… so there is already terrain there (presumably) and it’s only the update as they drag the painter thing… to me that seems ok for asynchronous rather than hanging.