@Override
	public CompletableFuture<Void> writeMemoryAsync(Address address, byte[] data) {
		MatchedMethod writeMem =
			matches.getBest(WriteMemMatcher.class, null, ActionName.WRITE_MEM, WriteMemMatcher.ALL);
		if (writeMem == null) {
			return AsyncUtils.nil();
		}
		Map<String, Object> args = new HashMap<>();
		args.put(writeMem.params.get("start").name(), address);
		args.put(writeMem.params.get("data").name(), data);
		RemoteParameter paramProcess = writeMem.params.get("process");
		if (paramProcess != null) {
			TraceObject process = getProcessForSpace(address.getAddressSpace());
			if (process == null) {
				throw new IllegalStateException("Cannot find process containing " + address);
			}
			args.put(paramProcess.name(), process);
		}
		return writeMem.method.invokeAsync(args).toCompletableFuture().thenApply(__ -> null);
	}
NOTE: Don't read in parallel, lest we overload the connection. This does queue them all
up in a CF chain, though. Still, a request doesn't go out until the preceding one
completes.
Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/TraceRmiTarget.java