@Override public JDBCClient getConnection(Handler<AsyncResult<SQLConnection>> handler) { Context ctx = vertx.getOrCreateContext(); exec.execute( () -> { Future<SQLConnection> res = Future.future(); try { /* This can block until a connection is free. We don't want to do that while running on a worker as we can enter a deadlock situation as the worker might have obtained a connection, and won't release it until it is run again There is a general principle here: *User code* should be executed on a worker and can potentially block, it's up to the *user* to deal with deadlocks that might occur there. If the *service code* internally blocks waiting for a resource that might be obtained by *user code*, then this can cause deadlock, so the service should ensure it never does this, by executing such code (e.g. getConnection) on a different thread to the worker pool. We don't want to use the vert.x internal pool for this as the threads might end up all blocked preventing other important operations from occurring (e.g. async file access) */ Connection conn = ds.getConnection(); SQLConnection sconn = new JDBCConnectionImpl(vertx, conn); res.complete(sconn); } catch (SQLException e) { res.fail(e); } ctx.runOnContext(v -> res.setHandler(handler)); }); return this; }
public Reader entries(@NonNull Future<Reader, ArchiveEntry[]> future) { if (mCache != null) { future.complete(Reader.this, mCache, null); return this; } mComplete = future; new Thread( new Runnable() { @Override public void run() { try { final ArchiveEntry[] results = entries(); if (mComplete == null || mPostBack == null) return; mPostBack.post( new Runnable() { @Override public void run() { mComplete.complete(Reader.this, results, null); } }); } catch (final Exception e) { if (mComplete == null || mPostBack == null) return; mPostBack.post( new Runnable() { @Override public void run() { mComplete.complete(Reader.this, null, e); } }); } } }) .start(); return this; }