JPPF Issue Tracker
star_faded.png
Please log in to bookmark issues
bug_report_small.png
CLOSED  Bug report JPPF-207  -  JPPFUuid generates uuid collisions in multithreaded mode
Posted Dec 06, 2013 - updated Dec 27, 2014
This issue is blocking the next release
icon_info.png This issue has been closed with status "Closed" and resolution "RESOLVED".
Issue details
  • Type of issue
    Bug report
  • Status
     
    Closed
  • Assigned to
     lolo4j
  • Progress
       
  • Type of bug
    Not triaged
  • Likelihood
    Not triaged
  • Effect
    Not triaged
  • Posted by
     lolo4j
  • Owned by
    Not owned by anyone
  • Category
    Core
  • Resolution
    RESOLVED
  • Priority
    Critical
  • Reproducability
    Always
  • Severity
    Critical
  • Targetted for
    icon_milestones.png JPPF 3.3.x
Issue description
When JPPFuuid is invoked by multiple concurrent threads in the same JVM, I can generate non-unique uuids (aka collisions). This only occurs in high-stress conditions, but it makes the whole class thread-unsafe. I found this because a sample I was working on was cxreating jobs with the same uuids, causing the client to reject the jobs.

Steps to reproduce this issue
The following code alwauys generates collisions:
public class TestJPPFUuid {
  @Test(timeout=5000)
  public void testUuidCollisions() throws Exception {
    int nbThreads = Runtime.getRuntime().availableProcessors();
    if (nbThreads < 2) nbThreads = 2;
    int nbTasks = 1000;
    ThreadPoolExecutor executor = null;
    try {
      executor = new ThreadPoolExecutor(nbThreads, nbThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
      executor.prestartAllCoreThreads();
      for (int i=0; i<10; i++) performExecution(nbTasks, executor);
    } finally {
      if (executor != null) executor.shutdownNow();
    }
  }
 
  public void performExecution(final int nbTasks, final ExecutorService executor) throws Exception {
    List<Future<String>> futures = new ArrayList<Future<String>>(nbTasks);
    Map<String, Boolean> map = new ConcurrentHashMap<String, Boolean>(nbTasks);
    for (int i=0; i<nbTasks; i++) futures.add(executor.submit(new UuidTask()));
    int count = 0;
    for (Future<String> future: futures) {
      String uuid = future.get();
      Boolean prevValue = map.put(uuid, Boolean.TRUE);
      if (prevValue != null) {
        System.out.println("uuid collision for " + uuid);
        count++;
      }
    }
    assertEquals("found " + count  + " collisions", 0, count);
  }
 
  private static class UuidTask implements Callable<String> {
    @Override
    public String call() throws Exception {
      return JPPFUuid.normalUUID();
    }
  }
}

#3
Comment posted by
 lolo4j
Dec 06, 13:56
The problem occurs because the Random object used by JPPFUuid is initialized as new Random(System.nanoTime()). On fast multi-core systems, concurrent threads can obtain the same value for System.nanoTime(), causing multiple Random instances to have the same seed and generate the same sequence of pseudo-random numbers. This should be fixable by adding to the seed an atomically incremented sequence number, like this:
private static final AtomicLong SEED_SEQUENCE = new AtomicLong(0L);
...
Random random = new Random(System.nanoTime() + SEED_SEQUENCE.incrementAndGet());


#6
Comment posted by
 lolo4j
Dec 06, 14:11
Fixed in