Cuberite Forum
Asynchronous tasks - Printable Version

+- Cuberite Forum (https://forum.cuberite.org)
+-- Forum: Plugins (https://forum.cuberite.org/forum-1.html)
+--- Forum: Plugin Discussion (https://forum.cuberite.org/forum-8.html)
+--- Thread: Asynchronous tasks (/thread-1830.html)

Pages: 1 2 3


Asynchronous tasks - Howaner - 03-11-2015

It is possible to run things asynchronly (in a other thread) and go back to synchronous (/ callbacks)?
I need this because I want to do database requests and one requests took 0.2 - 1.2ms.

My implementation in java:
package tv.rewinside.database.bukkit.util;

import com.google.common.collect.Queues;
import java.util.Queue;
import java.util.logging.Level;
import tv.rewinside.database.bukkit.DatabasePlugin;

public class DatabaseQueue extends Thread {
	private final Queue<Runnable> runnables = Queues.newConcurrentLinkedQueue();

	public DatabaseQueue() {
		super("Database Queue");
		this.setDaemon(true);
	}

	public synchronized void addRunnable(Runnable runnable) {
		this.runnables.add(runnable);
		this.notify();
	}

	private synchronized void blockCurrentThread() {
		try {
			while (this.runnables.isEmpty()) {
				this.wait();
			}
		} catch (InterruptedException ex) {
			ex.printStackTrace();
		}
	}

	public int getCount() {
		return this.runnables.size();
	}

	@Override
	public void run() {
		while (!this.isInterrupted()) {
			this.blockCurrentThread();

			while (!this.runnables.isEmpty()) {
				try {
					this.runnables.poll().run();
				} catch (Exception ex) {
					DatabasePlugin.getInstance().getLogger().log(Level.WARNING, "Exception while execute database task", ex);
				}
			}
		}
	}

}

public interface DatabaseCallback<T> {

	public void run(T result);

}

And a example use of the queue:
public void getPlayer(final String uuid, final DatabaseCallback<DatabasePlayer> callback) {
		DatabasePlayer dbPlayer = this.cachedPlayers.get(uuid);
		if (dbPlayer != null) {
			callback.run(dbPlayer);
			return;
		}

		// Load player -> Async
		this.plugin.getQueue().addRunnable(new Runnable() {
			@Override
			public void run() {
				DatabaseCache.this.syncCallbackCall(callback, DatabaseCache.this.loadPlayer(uuid));
			}
		});
	}

private DatabasePlayer loadPlayer(String uuid) {
		DatabasePlayer dbPlayer = this.plugin.getConnection().getPlayer(uuid);
		if (dbPlayer == null) return null;

		this.cachedPlayers.put(uuid, dbPlayer);
		this.cachedPlayersFromName.put(dbPlayer.getLastName(), dbPlayer);
		return dbPlayer;
	}

private <T> void syncCallbackCall(final DatabaseCallback<T> callback, final T arg) {
		if (Bukkit.isPrimaryThread()) {
			callback.run(arg);
			return;
		}

		Bukkit.getScheduler().runTask(this.plugin, new Runnable() {
			@Override
			public void run() {
				callback.run(arg);
			}
		});
	}



RE: Asynchronous tasks - NiLSPACE - 03-11-2015

It isn't currently possible to create a new thread in Lua. Xoft did play around with it a while ago, but I wonder if:
  • If we could work with it in MCServer
  • If it could work with LuaJit if we would ever move to that.



RE: Asynchronous tasks - worktycho - 03-11-2015

This is not possible with the current API. To implement the java API would involve significant development effort as we would need to implement a thread pool in C++. From the point of view of MCServer the simplest solution would be to implement a WebWorker style wrapper around cIsThread. However would this be too primitive an interface for plugin developers?


RE: Asynchronous tasks - NiLSPACE - 04-12-2015

Coming back to this, xoft do you think your LuaMultiThreaded could be implemented in MCServer? And if so, if I were to do an action that takes really long, for example a large for-loop, would it still block the server?


RE: Asynchronous tasks - xoft - 04-12-2015

It would need a lot of thought and probably a lot of testing. For example the threading might interfere with our callbacks, the objects currently guaranteed to be valid during the callback duration might get deallocated or another similar failure.

If if was implemented, a for loop would block the thread in which it was started. So if you started the for-loop in the world tick hook callback, bad idea. If you started it in the webadmin callback thread, no major problem there, the world goes on, only the webadmin will take ages to respond. It might even be possible to create a separate thread directly from Lua.


RE: Asynchronous tasks - NiLSPACE - 04-12-2015

It would be great if we could create new threads in Lua. That could allow WorldEdit for example to make big structures without blocking the server.


RE: Asynchronous tasks - xoft - 04-13-2015

Not likely, since writing the structure might still block the server for a considerable amount of time. True, much less than actually generating such a big structure.


RE: Asynchronous tasks - xoft - 04-13-2015

You could already do a few things to avoid lag - for example make the command create a description of the operation, store it in a storage and have the world tick thread do a little bit of work on the queue of such operations at a time - for example generating, say, 10 slices of a big area.
function OnCmdGenerate(...)
  local operation = cGenerateStructure:new(...)
  table.insert(g_WorkQueue[worldName], operation)
end

function OnWorldTick(...)
  local workItem = g_WorkQueue[worldName][1]
  if (workItem ~= nil) then
    workItem:doABitOfWork()
    if (workItem:IsFinished()) then
      table.remove(g_WorkQueue, 1)
    end
  end
end



RE: Asynchronous tasks - NiLSPACE - 04-13-2015

That would require allot of work though Wink If we could create a new thread for a certain function, then it should be really easy to make WorldEdit use it Smile


RE: Asynchronous tasks - worktycho - 04-15-2015

The API I suggested earlier get rid of a lot of these issues. You basically create a whole separate Lua State which gets a file passed to it process. It can only communicate with the parent via asynchronous message passing.