Fixed POWEngine and improved test.

(Locks can't be released from a different thread, we need to use a semaphore)
This commit is contained in:
Christian Basler 2015-10-27 07:50:20 +01:00
parent 36fe780766
commit b496f81b20
2 changed files with 40 additions and 5 deletions

View File

@ -24,6 +24,7 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.Semaphore;
import static ch.dissem.bitmessage.utils.Bytes.inc; import static ch.dissem.bitmessage.utils.Bytes.inc;
@ -31,15 +32,28 @@ import static ch.dissem.bitmessage.utils.Bytes.inc;
* A POW engine using all available CPU cores. * A POW engine using all available CPU cores.
*/ */
public class MultiThreadedPOWEngine implements ProofOfWorkEngine { public class MultiThreadedPOWEngine implements ProofOfWorkEngine {
private static Logger LOG = LoggerFactory.getLogger(MultiThreadedPOWEngine.class); private static final Logger LOG = LoggerFactory.getLogger(MultiThreadedPOWEngine.class);
private static final Semaphore semaphore = new Semaphore(1, true);
/**
* Although it has a callback, this method will block until all pending nonce calculations are done. (It gets very
* inefficient if multiple nonce are calculated at the same time.
*
* @param initialHash the SHA-512 hash of the object to send, sans nonce
* @param target the target, representing an unsigned long
* @param callback called with the calculated nonce as argument. The ProofOfWorkEngine implementation must make
*/
@Override @Override
public void calculateNonce(byte[] initialHash, byte[] target, Callback callback) { public void calculateNonce(byte[] initialHash, byte[] target, Callback callback) {
try {
semaphore.acquire();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
callback = new CallbackWrapper(callback); callback = new CallbackWrapper(callback);
int cores = Runtime.getRuntime().availableProcessors(); int cores = Runtime.getRuntime().availableProcessors();
if (cores > 255) cores = 255; if (cores > 255) cores = 255;
LOG.info("Doing POW using " + cores + " cores"); LOG.info("Doing POW using " + cores + " cores");
long time = System.currentTimeMillis();
List<Worker> workers = new ArrayList<>(cores); List<Worker> workers = new ArrayList<>(cores);
for (int i = 0; i < cores; i++) { for (int i = 0; i < cores; i++) {
Worker w = new Worker(workers, (byte) cores, i, initialHash, target, callback); Worker w = new Worker(workers, (byte) cores, i, initialHash, target, callback);
@ -89,6 +103,7 @@ public class MultiThreadedPOWEngine implements ProofOfWorkEngine {
try { try {
callback.onNonceCalculated(nonce); callback.onNonceCalculated(nonce);
} finally { } finally {
semaphore.release();
for (Worker w : workers) { for (Worker w : workers) {
w.interrupt(); w.interrupt();
} }

View File

@ -25,12 +25,12 @@ import static ch.dissem.bitmessage.utils.Singleton.security;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
public class ProofOfWorkEngineTest extends TestBase { public class ProofOfWorkEngineTest extends TestBase {
@Test @Test(timeout = 90_000)
public void testSimplePOWEngine() throws InterruptedException { public void testSimplePOWEngine() throws InterruptedException {
testPOW(new SimplePOWEngine()); testPOW(new SimplePOWEngine());
} }
@Test @Test(timeout = 90_000)
public void testThreadedPOWEngine() throws InterruptedException { public void testThreadedPOWEngine() throws InterruptedException {
testPOW(new MultiThreadedPOWEngine()); testPOW(new MultiThreadedPOWEngine());
} }
@ -49,8 +49,28 @@ public class ProofOfWorkEngineTest extends TestBase {
} }
}); });
byte[] nonce = waiter.waitForValue(); byte[] nonce = waiter.waitForValue();
System.out.println("Calculating nonce took " + (System.currentTimeMillis() - time) + "ms"); time = System.currentTimeMillis() - time;
System.out.println("Calculating nonce took " + time + "ms");
assertTrue(Bytes.lt(security().doubleSha512(nonce, initialHash), target, 8)); assertTrue(Bytes.lt(security().doubleSha512(nonce, initialHash), target, 8));
// Let's add a second (shorter) run to find possible multi threading issues
long time2 = System.currentTimeMillis();
byte[] initialHash2 = security().sha512(new byte[]{1, 3, 6, 5});
byte[] target2 = {0, -1, -1, -1, -1, -1, -1, -1};
final CallbackWaiter<byte[]> waiter2 = new CallbackWaiter<>();
engine.calculateNonce(initialHash2, target2,
new ProofOfWorkEngine.Callback() {
@Override
public void onNonceCalculated(byte[] nonce) {
waiter2.setValue(nonce);
}
});
byte[] nonce2 = waiter2.waitForValue();
time2 = System.currentTimeMillis() - time2;
System.out.println("Calculating nonce took " + time2 + "ms");
assertTrue(Bytes.lt(security().doubleSha512(nonce2, initialHash2), target2, 8));
assertTrue("Second nonce must be quicker to find", time > time2);
} }
} }