Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Modeling » EMF » [CDO] Race condition between commit() and resolveConflicts()?
[CDO] Race condition between commit() and resolveConflicts()? [message #645218] Thu, 16 December 2010 00:46 Go to next message
dloose  is currently offline dloose Friend
Messages: 5
Registered: August 2010
Junior Member
Has anyone else seen a race condition between CDOTransactionImpl.commit() and .handleConflicts()? I saw it in my CDO 3.0.1 app and managed to write a test case that reproduces it pretty reliably. All I do is create 2 CDOSessions pointing at the same server, open a transaction on each of them, add a resource as a child of the root resource on each of them, then commit each of them.

Here's my understanding of the problem:

After the first transaction is committed, invalidate() is called on the 2nd transaction in a background thread. invalidate() acquires CDOViewImpl.lock but gives it up after it builds the set of objects that have conflicts. Meanwhile, on the main thread, commit() is called on the main thread. It acquires CDOViewImpl.lock, which is available now. If I'm lucky, the background thread manages to finish resolving the conflicts before the main thread calls hasConflicts(). If I'm not, hasConflicts() gets called first.

Is there any reason invalidate() can't hang on to the lock until handleConflicts() returns?

Here's the code I've been using to reproduce the race condition. You can really force the issue by putting a breakpoint on the t2.commit() line and on the first line of CDOTransactionImpl.handleConflicts

import org.eclipse.emf.cdo.common.model.CDOPackageRegistry;
import org.eclipse.emf.cdo.defs.CDODefsPackage;
import org.eclipse.emf.cdo.eresource.EresourcePackage;
import org.eclipse.emf.cdo.net4j.CDONet4jUtil;
import org.eclipse.emf.cdo.net4j.CDOSession;
import org.eclipse.emf.cdo.net4j.CDOSessionConfiguration;
import org.eclipse.emf.cdo.transaction.CDOTransaction;
import org.eclipse.emf.cdo.util.CommitException;
import org.eclipse.emf.spi.cdo.AbstractObjectConflictResolver;
import org.eclipse.net4j.Net4jUtil;
import org.eclipse.net4j.tcp.ITCPConnector;
import org.eclipse.net4j.tcp.TCPUtil;
import org.eclipse.net4j.util.container.ContainerUtil;
import org.eclipse.net4j.util.container.IManagedContainer;
import org.eclipse.net4j.util.lifecycle.LifecycleUtil;

import com.oco.ddm.models.metadata.MetadataPackage;

public class Test {

  public static CDOSession openSession(String cdoUrl, String repoName) {
    IManagedContainer container = ContainerUtil.createContainer();
    Net4jUtil.prepareContainer(container); // Register Net4j factories
    TCPUtil.prepareContainer(container); // Register TCP factories
    CDONet4jUtil.prepareContainer(container); // Prepare the CDO client
    LifecycleUtil.activate(container);

    ITCPConnector connector = TCPUtil.getConnector(container, cdoUrl);

    CDOSessionConfiguration configuration = CDONet4jUtil.createSessionConfiguration();
    configuration.setConnector(connector);
    configuration.setRepositoryName(repoName);

    CDOSession session = configuration.openSession();

    CDOPackageRegistry packageRegistry = session.getPackageRegistry();
    packageRegistry.putEPackage(EresourcePackage.eINSTANCE);
    packageRegistry.putEPackage(CDODefsPackage.eINSTANCE);
    packageRegistry.putEPackage(MetadataPackage.eINSTANCE);

    return session;
  }

  public static void main(String[] args) {
    CDOSession s1 = openSession("localhost:2036", "repo1");
    CDOSession s2 = openSession("localhost:2036", "repo1");

    CDOTransaction t1 = s1.openTransaction();
    CDOTransaction t2 = s2.openTransaction();

    t1.options()
        .addConflictResolver(new AbstractObjectConflictResolver.TakeRemoteChangesThenApplyLocalChanges());
    t2.options()
        .addConflictResolver(new AbstractObjectConflictResolver.TakeRemoteChangesThenApplyLocalChanges());

    t1.createResource("sowhat111");
    t2.createResource("sowhat222");

    try {
      t1.commit();
      t2.commit();
    }
    catch(CommitException e) {
      e.printStackTrace();
    }

    t1.close();
    t2.close();
    s1.close();
    s2.close();
  }
}
Re: [CDO] Race condition between commit() and resolveConflicts()? [message #645250 is a reply to message #645218] Thu, 16 December 2010 09:28 Go to previous message
Eike Stepper is currently offline Eike StepperFriend
Messages: 6682
Registered: July 2009
Senior Member
This is a multi-part message in MIME format.
--------------080904080402010306090105
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 7bit

Hi dloose,

Without explicit (pessimistic) locking there is always a chance of race conditions between the server and multiple clients and there's no way (except explicit locking) to circumvent that. The case you described looks like a (known) special case with a partial overlap of the threads. More common is the case where the invalidations really come so late that they even don't overlap a little bit.

I see that your test code is a little special in one more aspect: The two clients (sessions) are kind of synchronized with *each other* because they run in the exact same thread. Note that they are not synchronized regarding their different background threads for invalidation handling!

There is a way to synchronize the current (main) thread with an invalidation thread. See how CDOSession and CDOView extend the CDOUpdateable interface:



Knowing that you can synchronize the threads by e.g.:

tx2.waitForUpdate(tx1.getLastCommitTime(), DEFAULT_TIMEOUT);

Does that help?

Cheers
/Eike

----
http://www.esc-net.de
http://thegordian.blogspot.com
http://twitter.com/eikestepper



Am 16.12.2010 01:46, schrieb dloose:
> Has anyone else seen a race condition between CDOTransactionImpl.commit() and .handleConflicts()? I saw it in my CDO 3.0.1 app and managed to write a test case that reproduces it pretty reliably. All I do is create 2 CDOSessions pointing at the same server, open a transaction on each of them, add a resource as a child of the root resource on each of them, then commit each of them.
>
> Here's my understanding of the problem:
>
> After the first transaction is committed, invalidate() is called on the 2nd transaction in a background thread. invalidate() acquires CDOViewImpl.lock but gives it up after it builds the set of objects that have conflicts. Meanwhile, on the main thread, commit() is called on the main thread. It acquires CDOViewImpl.lock, which is available now. If I'm lucky, the background thread manages to finish resolving the conflicts before the main thread calls hasConflicts(). If I'm not, hasConflicts() gets called first.
>
> Is there any reason invalidate() can't hang on to the lock until handleConflicts() returns?
>
> Here's the code I've been using to reproduce the race condition. You can really force the issue by putting a breakpoint on the t2.commit() line and on the first line of CDOTransactionImpl.handleConflicts
>
>
> import org.eclipse.emf.cdo.common.model.CDOPackageRegistry;
> import org.eclipse.emf.cdo.defs.CDODefsPackage;
> import org.eclipse.emf.cdo.eresource.EresourcePackage;
> import org.eclipse.emf.cdo.net4j.CDONet4jUtil;
> import org.eclipse.emf.cdo.net4j.CDOSession;
> import org.eclipse.emf.cdo.net4j.CDOSessionConfiguration;
> import org.eclipse.emf.cdo.transaction.CDOTransaction;
> import org.eclipse.emf.cdo.util.CommitException;
> import org.eclipse.emf.spi.cdo.AbstractObjectConflictResolver;
> import org.eclipse.net4j.Net4jUtil;
> import org.eclipse.net4j.tcp.ITCPConnector;
> import org.eclipse.net4j.tcp.TCPUtil;
> import org.eclipse.net4j.util.container.ContainerUtil;
> import org.eclipse.net4j.util.container.IManagedContainer;
> import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
>
> import com.oco.ddm.models.metadata.MetadataPackage;
>
> public class Test {
>
> public static CDOSession openSession(String cdoUrl, String repoName) {
> IManagedContainer container = ContainerUtil.createContainer();
> Net4jUtil.prepareContainer(container); // Register Net4j factories
> TCPUtil.prepareContainer(container); // Register TCP factories
> CDONet4jUtil.prepareContainer(container); // Prepare the CDO client
> LifecycleUtil.activate(container);
>
> ITCPConnector connector = TCPUtil.getConnector(container, cdoUrl);
>
> CDOSessionConfiguration configuration = CDONet4jUtil.createSessionConfiguration();
> configuration.setConnector(connector);
> configuration.setRepositoryName(repoName);
>
> CDOSession session = configuration.openSession();
>
> CDOPackageRegistry packageRegistry = session.getPackageRegistry();
> packageRegistry.putEPackage(EresourcePackage.eINSTANCE);
> packageRegistry.putEPackage(CDODefsPackage.eINSTANCE);
> packageRegistry.putEPackage(MetadataPackage.eINSTANCE);
>
> return session;
> }
>
> public static void main(String[] args) {
> CDOSession s1 = openSession("localhost:2036", "repo1");
> CDOSession s2 = openSession("localhost:2036", "repo1");
>
> CDOTransaction t1 = s1.openTransaction();
> CDOTransaction t2 = s2.openTransaction();
>
> t1.options()
> .addConflictResolver(new AbstractObjectConflictResolver.TakeRemoteChangesThenApplyLoc alChanges());
> t2.options()
> .addConflictResolver(new AbstractObjectConflictResolver.TakeRemoteChangesThenApplyLoc alChanges());
>
> t1.createResource("sowhat111");
> t2.createResource("sowhat222");
>
> try {
> t1.commit();
> t2.commit();
> }
> catch(CommitException e) {
> e.printStackTrace();
> }
>
> t1.close();
> t2.close();
> s1.close();
> s2.close();
> }
> }
>

--------------080904080402010306090105
Content-Type: multipart/related;
boundary="------------070501000507080800090709"


--------------070501000507080800090709
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: 8bit

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
</head>
<body bgcolor="#ffffff" text="#000000">
Hi dloose,<br>
<br>
Without explicit (pessimistic) locking there is always a chance of
race conditions between the server and multiple clients and there's
no way (except explicit locking) to circumvent that. The case you
described looks like a (known) special case with a partial overlap
of the threads. More common is the case where the invalidations
really come so late that they even don't overlap a little bit. <br>
<br>
I see that your test code is a little special in one more aspect:
The two clients (sessions) are kind of synchronized with *each
other* because they run in the exact same thread. Note that they are
not synchronized regarding their different background threads for
invalidation handling!<br>
<br>
There is a way to synchronize the current (main) thread with an
invalidation thread. See how CDOSession and CDOView extend the
CDOUpdateable interface:<br>
<br>
<img src="cid:part1.05080400.07050900@esc-net.de" alt=""><br>
<br>
Knowing that you can synchronize the threads by e.g.:<br>
<br>
    tx2.waitForUpdate(tx1.getLastCommitTime(), DEFAULT_TIMEOUT);<br>
<br>
Does that help?<br>
<br>
Cheers<br>
/Eike<br>
<br>
----<br>
<a class="moz-txt-link-freetext" href="http://www.esc-net.de">http://www.esc-net.de</a><br>
<a class="moz-txt-link-freetext" href="http://thegordian.blogspot.com">http://thegordian.blogspot.com</a><br>
<a class="moz-txt-link-freetext" href="http://twitter.com/eikestepper">http://twitter.com/eikestepper</a><br>
<br>
<br>
<br>
Am 16.12.2010 01:46, schrieb dloose:
<blockquote cite="mid:iebn7p$h6d$1@news.eclipse.org" type="cite">Has
anyone else seen a race condition between
CDOTransactionImpl.commit() and .handleConflicts()? I saw it in my
CDO 3.0.1 app and managed to write a test case that reproduces it
pretty reliably. All I do is create 2 CDOSessions pointing at the
same server, open a transaction on each of them, add a resource as
a child of the root resource on each of them, then commit each of
them.
<br>
<br>
Here's my understanding of the problem:
<br>
<br>
After the first transaction is committed, invalidate() is called
on the 2nd transaction in a background thread. invalidate()
acquires  CDOViewImpl.lock but gives it up after it builds the set
of objects that have conflicts. Meanwhile, on the main thread,
commit() is called on the main thread. It acquires
CDOViewImpl.lock, which is available now. If I'm lucky, the
background thread manages to finish resolving the conflicts before
the main thread calls hasConflicts(). If I'm not, hasConflicts()
gets called first.
<br>
<br>
Is there any reason invalidate() can't hang on to the lock until
handleConflicts() returns?
<br>
<br>
Here's the code I've been using to reproduce the race condition.
You can really force the issue by putting a breakpoint on the
t2.commit() line and on the first line of
CDOTransactionImpl.handleConflicts
<br>
<br>
<br>
import org.eclipse.emf.cdo.common.model.CDOPackageRegistry;
<br>
import org.eclipse.emf.cdo.defs.CDODefsPackage;
<br>
import org.eclipse.emf.cdo.eresource.EresourcePackage;
<br>
import org.eclipse.emf.cdo.net4j.CDONet4jUtil;
<br>
import org.eclipse.emf.cdo.net4j.CDOSession;
<br>
import org.eclipse.emf.cdo.net4j.CDOSessionConfiguration;
<br>
import org.eclipse.emf.cdo.transaction.CDOTransaction;
<br>
import org.eclipse.emf.cdo.util.CommitException;
<br>
import org.eclipse.emf.spi.cdo.AbstractObjectConflictResolver;
<br>
import org.eclipse.net4j.Net4jUtil;
<br>
import org.eclipse.net4j.tcp.ITCPConnector;
<br>
import org.eclipse.net4j.tcp.TCPUtil;
<br>
import org.eclipse.net4j.util.container.ContainerUtil;
<br>
import org.eclipse.net4j.util.container.IManagedContainer;
<br>
import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
<br>
<br>
import com.oco.ddm.models.metadata.MetadataPackage;
<br>
<br>
public class Test {
<br>
<br>
 public static CDOSession openSession(String cdoUrl, String
repoName) {
<br>
   IManagedContainer container = ContainerUtil.createContainer();
<br>
   Net4jUtil.prepareContainer(container); // Register Net4j
factories
<br>
   TCPUtil.prepareContainer(container); // Register TCP factories
<br>
   CDONet4jUtil.prepareContainer(container); // Prepare the CDO
client
<br>
   LifecycleUtil.activate(container);
<br>
<br>
   ITCPConnector connector = TCPUtil.getConnector(container,
cdoUrl);
<br>
<br>
   CDOSessionConfiguration configuration =
CDONet4jUtil.createSessionConfiguration();
<br>
   configuration.setConnector(connector);
<br>
   configuration.setRepositoryName(repoName);
<br>
<br>
   CDOSession session = configuration.openSession();
<br>
<br>
   CDOPackageRegistry packageRegistry =
session.getPackageRegistry();
<br>
   packageRegistry.putEPackage(EresourcePackage.eINSTANCE);
<br>
   packageRegistry.putEPackage(CDODefsPackage.eINSTANCE);
<br>
   packageRegistry.putEPackage(MetadataPackage.eINSTANCE);
<br>
<br>
   return session;
<br>
 }
<br>
<br>
 public static void main(String[] args) {
<br>
   CDOSession s1 = openSession("localhost:2036", "repo1");
<br>
   CDOSession s2 = openSession("localhost:2036", "repo1");
<br>
<br>
   CDOTransaction t1 = s1.openTransaction();
<br>
   CDOTransaction t2 = s2.openTransaction();
<br>
<br>
   t1.options()
<br>
       .addConflictResolver(new
AbstractObjectConflictResolver.TakeRemoteChangesThenApplyLoc alChanges()); <br>
   t2.options()
<br>
       .addConflictResolver(new
AbstractObjectConflictResolver.TakeRemoteChangesThenApplyLoc alChanges()); <br>
<br>
   t1.createResource("sowhat111");
<br>
   t2.createResource("sowhat222");
<br>
<br>
   try {
<br>
     t1.commit();
<br>
     t2.commit();
<br>
   }
<br>
   catch(CommitException e) {
<br>
     e.printStackTrace();
<br>
   }
<br>
<br>
   t1.close();
<br>
   t2.close();
<br>
   s1.close();
<br>
   s2.close();
<br>
 }
<br>
}
<br>
<br>
</blockquote>
</body>
</html>

--------------070501000507080800090709
Content-Type: image/png;
name="moz-screenshot-3.png"
Content-Transfer-Encoding: base64
Content-ID: <part1.05080400.07050900@esc-net.de>
Content-Disposition: inline;
filename="moz-screenshot-3.png"

iVBORw0KGgoAAAANSUhEUgAAALkAAABKCAIAAABoyWK8AAAId0lEQVR4nO2c 72vbRhjH7z/Y
P+OIIPZqb9q1sIxlg/5IVNltBWOBdGsHZazQNdnspLBqhawblLouSZ0S1zjr pm2s29gg2UxM
s7SFQtyY4ZT+sGtw4riO4oH2Qr9O0km5hESx7OfDvcjd89wjtfflzpG/EaoC AB3oOgDQgRR3
VlZWisXiIrAtisXiysqKx39v4HDVSqVSWV5ertfrzeZ/asvNPrkSm/2Inz7F ZT48lrkSm83N
PjGi0GytXq8vLy9XKhU/l3NXcdVKYWlJXpdleUOWZVmWE2O5U1zm3MBPl87/ qbZzAz+d4jKJ
sZwMkNmQ1+XC0pKfy7mruGplcXGx2Wyuy/K6LCfGciffvfXl+T8mvp1PTzxU 28S381+e/+Pk
u7cSYzk1DZqtNZvNxcVFP5dzV/HSykaziV4bVBQl3JMc+eT3yav/4AmPFl5M Xv3n4rnfwz3J
x49eNrbGw4vdSKP74sNGo9HIHEcm2pgBFsRCmeOWRFvXA/pMmomu1TY6Ryvy xgbqjalyGTpz
d+jM3WtfzanRmbv/qiNDZ+5e+Ojnq5ey9VevaNuD0W6EIhmzO5p5VX+ViaDu 0QfqYCaCEIpk
tIRMBJmhB6PdqHv0gZ5mjDu7Hs0j07sIMeo6Rd7Y6BityDLqjZ394U9VLoP9 6cH+tBr9ObOo
dj84mvrgaOp0eHqNlvsjDIqknePpCGJG7juzHPnmgHWKveuBR6Z3EWLUdYos y52ilXVZRr2x
Q+N3Do3fQa8NHt537fC+a2o0Pb6gdvsPJI7si4d7bq7WalRtYYRB4RQhlA4j JrZgdBdiDAqn
ifkLMUbNtE0xuukwYmLpEQYhhBAzsoBdWh0ZMSamwvrZFk6v1tJGD4XTjqhb Zew29EsgFE7V
auudo5VGo4F6Y69fuK3uK0f2x4/sj6vR9MT9I/vjqlb6Dlw/HZ5eoWQ+yjDR eUIgxSM8MB9l
EJ8i5ush+xSjm+IR0n6cjzLYIJ/S5yNbUXyu8/42reyYm+KZ6Hyj0egsrahC ibw9wffc4N5K
qNHp5EPurQTfc4PvuXHineSNr3O0D4rvRRnETxECUzxiovfwPCZ6j5ivh+xT jK5lfIpH/BQ2
yXatKV7fOkg1PaNaZWPwXlTfVBBCCPFTHaSVer2uCuW9N+KDXPqLs7/gCTO/ Fr44+8uH/PR7
b8QfP3pZoSX3eRc6dss5fusY6vo858xy5FtCxCl4KX3UkqsnEEphc72jZthZ TaNer3eKVtbW
1srlcrlcjl+e6z8wflb4fvTT3/A20Jc68c5k/PJceUtMcgghblLvZoeHJ9XR ruGsmWEmWHqT
HLLm6R0sCxvODndpo2Y8O9ylxY1rZoe7tJ+w2yBHiZUd0XK5XC6vra11ilZq tVpJJ355jjs4
/vGJ7z4+Ma2377iD4/HLc6VtkB0KGXt1aChbKpVKSQ7Zh0ywIJd0KWROSnIo xHEhe75eJDQ0
xGnZ+vQQx4Ws98ElSVFi5SRnXBv/d3HJWq3WKVpZXV19/vyF0XKzT8aiMwNH b/M9NweO3v5m
9K/c7BM8oWVash+FLvy957fxYnV1tYO08vTpswC2iT4U+mxmz2/jWadopVAo VKvVUqn0NHiM
96HQZzN7fBOlUqlarRYKBT+Xc1fx9CQUi9VqlfbBCWClWq0uF4sd4UlQFKVS qRSWlnwzB7UZ
haWldhKK4q0VAMDZG60QLXZ7cicAPXugFQ+Lnf83A9Djt1Y2tdj5fD8APb5q 5fGjlzQWuy1W
zYus/qiUFfOKoiiSgH2Fp40ZYEFLyBwXpG382/Ii67hUm+GrVq7EZs8N/Lip xW4LFfMii69t
XhQlRVEkwVw2ScBXXxIwhWDLmxdZI0krAtjxVSunw9PvH06phjoPix11PXyF cXCt4FmOfGPA
OgMg4qtWIm8n+9683n8g4W2xoy3nJhX7yut5hPy8yKqZlg0Hm4efSra+rYtd FDsV9etJAmJF
USAfiwHB732F77nZfzDhbbGjLWcstB13rZDkYJ49rGN189qPrJhXJMGiNFvX zJcEo4h5ymFH
IRYPFr5q5eql7MneyU0tdrTltrCvsGLee1/BaxqbEIYgqSNmtq1rXNR6FV1R to9QgdxZfNVK
sVClsdhR19u5zyvOMZfaqoSMANYFrew0m1rstlbO+luO2+9BlqUj/lIkiY5f jkifYPQM0q7l
dgYZBw9oZet4WOy2Uw4/LAjPVxyrggWxfYM0ilcWJPsDGPvzGOJnW6I+QCtb gWix25M7AeiB
75kBWkArAC2gFYAW0ApAC2gFoAV8cQAt4IsDaAFfHEAL+OK0MbtHTvtKKJAP WHcJ8MXp6rI+
8A+kaWCXAV+c4VvCBQVSIQC+ODUTi5lSIX4dKEgWNVlz2lpj4Isz9xzDUuDQ gbtHThJY1pRb
e3+6AV8ctiuwYt6y4rh9yeGR00wrrCg5J7Yn4IvD7SrGwlvruIpM3WTyIitI 7S8V8MXZziqi
hZbkkcuLLH76CILQ7lIBX5y9FJ5P/jMObJPCo20vFfDFAdTA98wALaAVgBbQ CkALaAWgBbQC
0BJ4rYDFzjeCrRWw2PlJgLUCFjufCapWWtliZ2NvHujuwqPkoGqldS12Zth/ kezuRYOqlda1
2BEn+gRohUQLW+yIEyUBsYLAGrY667eXxLfJ2V9XR3qpKp5j+1J0599fF1St tLLFjjSR+OYf
i+fB/jY5V9evMcv74Nv599cFVSstbbEjTLR2zS3A4dG0mvHsf4mCzyLcg1ud nXknWVC10toW
O8dE+4ng9PiSV858H51zFmiFnla22HlphXCGeK2cJg3CrO2dQdt/f12AtaK0 ssXO6wzSL8MK
gse+Yv8o65ylOJO0vutnW6I+OkMrCljsfCTwWgF8A7QC0AJaAWgBrQC0/A8s k8h9xWwGOQAA
AABJRU5ErkJggg==
--------------070501000507080800090709--

--------------080904080402010306090105--


Previous Topic:How can i iterate throught my emf model from a gmf editor without parsing the xml model file?
Next Topic:how to get AdapterFactory for another package
Goto Forum:
  


Current Time: Tue Apr 23 12:03:32 GMT 2024

Powered by FUDForum. Page generated in 0.03612 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top