/* * Copyright (c) 2009-2012, 2016 Eike Stepper (Berlin, Germany) and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Simon McDuff - initial API and implementation */ package org.eclipse.emf.cdo.tests.bugzilla; import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.eresource.CDOResource; import org.eclipse.emf.cdo.session.CDOSession; import org.eclipse.emf.cdo.tests.AbstractCDOTest; import org.eclipse.emf.cdo.tests.model1.OrderDetail; import org.eclipse.emf.cdo.transaction.CDOTransaction; import org.eclipse.emf.cdo.util.CDOUtil; import org.eclipse.emf.cdo.util.CommitException; import org.eclipse.net4j.util.transaction.TransactionException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Concurrency problem: attribute of enumeration type not updated correctly between two clients * <p> * See bug 273565 * * @author Simon McDuff */ public class Bugzilla_273565_Test extends AbstractCDOTest { /** * Thread A: Update the value to 3 and 2 only if the value is at 1.<br> * Thread B: Update the value to 1 only if the value is at 2. * <p> * Thread B will load objects.<br> * But at the same time will update remote changes.<br> * Causing not to have the latest version. * <p> * See bug 273565 */ public void _testBugzilla_273565() throws Exception { final CountDownLatch start = new CountDownLatch(1); final boolean[] done = { false }; final Exception exception[] = { null }; final OrderDetail orderDetail = getModel1Factory().createOrderDetail(); orderDetail.setPrice(2); final CDOSession session = openSession(); final CDOTransaction transaction = session.openTransaction(); CDOResource resource = transaction.createResource(getResourcePath("/test1")); resource.getContents().add(orderDetail); transaction.commit(); final CDOID id = CDOUtil.getCDOObject(orderDetail).cdoID(); Thread threadA = new Thread() { @Override public void run() { CDOSession session = openSession(); CDOTransaction transaction = session.openTransaction(); OrderDetail orderDetail = (OrderDetail)CDOUtil.getEObject(transaction.getObject(id)); try { start.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); while (!done[0]) { while (orderDetail.getPrice() != 1 && !done[0]) { sleep(1); } try { orderDetail.setPrice(3); transaction.commit(); } catch (CommitException ex) { transaction.rollback(); continue; } try { orderDetail.setPrice(2); transaction.commit(); } catch (CommitException ex) { transaction.rollback(); continue; } } } catch (Exception ex) { ex.printStackTrace(); exception[0] = ex; } finally { session.close(); } } }; threadA.setDaemon(true); threadA.start(); Thread threadB = new Thread() { @Override public void run() { try { start.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); for (int i = 0; i < 50 && exception[0] == null; i++) { try { orderDetail.setPrice(1); transaction.commit(); } catch (TransactionException ex) { transaction.rollback(); continue; } while (orderDetail.getPrice() != 2) { sleep(1); } } } catch (Exception ex) { ex.printStackTrace(); exception[0] = ex; } done[0] = true; } }; threadB.setDaemon(true); threadB.start(); start.countDown(); threadA.join(DEFAULT_TIMEOUT); threadA.interrupt(); threadB.join(DEFAULT_TIMEOUT); threadB.interrupt(); if (exception[0] != null) { throw exception[0]; } } /** * See bug 273565 */ public void _testBugzilla_273565_Lock() throws Exception { disableConsole(); OrderDetail orderDetail = getModel1Factory().createOrderDetail(); orderDetail.setPrice(2); CDOSession session = openSession(); CDOTransaction transaction = session.openTransaction(); CDOResource resource = transaction.createResource(getResourcePath("/test1")); resource.getContents().add(orderDetail); transaction.commit(); final CDOID id = CDOUtil.getCDOObject(orderDetail).cdoID(); session.close(); final CountDownLatch start = new CountDownLatch(1); final Exception exception[] = { null }; class Modifier extends Thread { private float price; public Modifier(float price) { setDaemon(true); this.price = price; } @Override public void run() { CDOSession session = openSession(); CDOTransaction transaction = session.openTransaction(); OrderDetail orderDetail = (OrderDetail)transaction.getObject(id); try { start.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); for (int i = 0; i < 5000 && exception[0] == null; i++) { CDOUtil.getCDOObject(orderDetail).cdoWriteLock().lock(DEFAULT_TIMEOUT); System.out.println("\nGot lock: " + price + " --> " + CDOUtil.getCDOObject(orderDetail).cdoRevision()); sleep(1L); orderDetail.setPrice(price); System.out.println("Committing: " + price); transaction.commit(); sleep(1L); } } catch (Exception ex) { ex.printStackTrace(); exception[0] = ex; } finally { session.close(); } } } Modifier threadA = new Modifier(1); threadA.start(); Modifier threadB = new Modifier(2); threadB.start(); start.countDown(); threadA.join(DEFAULT_TIMEOUT); threadA.interrupt(); threadB.join(DEFAULT_TIMEOUT); threadB.interrupt(); if (exception[0] != null) { throw exception[0]; } } /** * See bug 273565 */ public void testBugzilla_273565_NoThreads() throws Exception { OrderDetail orderDetail = getModel1Factory().createOrderDetail(); orderDetail.setPrice(1); CDOSession session = openSession(); CDOTransaction transaction = session.openTransaction(); CDOResource resource = transaction.createResource(getResourcePath("/test1")); resource.getContents().add(orderDetail); transaction.commit(); CDOTransaction transaction2 = session.openTransaction(); OrderDetail orderDetail2 = (OrderDetail)CDOUtil.getEObject(transaction2.getObject(orderDetail)); CDOUtil.getCDOObject(orderDetail).cdoWriteLock().lock(DEFAULT_TIMEOUT); orderDetail.setPrice(2); boolean locked = CDOUtil.getCDOObject(orderDetail2).cdoWriteLock().tryLock(DEFAULT_TIMEOUT_EXPECTED, TimeUnit.MILLISECONDS); assertEquals(false, locked); } }