Locking |
![]() |
CDO normally lets transactions edit their local state and detects concurrent changes when they commit. Explicit locking is an additional repository-coordinated mechanism for workflows that need other views to stay out of the way while an object is being inspected or changed. This chapter explains the lock types, their lifecycle, durable locking, and the events and state APIs that applications can use.
A lock is owned by a view, and therefore indirectly by its session; a transaction
is a read-write view. The repository's locking manager arbitrates locks between sessions and views. Locks apply to
CDO objects, including resource nodes, rather than to arbitrary local Java objects. A lock protects the relevant
repository object from conflicting remote lock operations and remote changes, but it is not a substitute for a
transaction commit, a local view critical section, or application-level authorization.
Explicit locking is not required for every transaction. Use normal transaction editing and commit-time conflict detection when concurrent work is acceptable, and acquire explicit locks only when a business operation needs an exclusion or reservation that should be visible to other sessions.
Table of Contents
CDO exposes read, write, and write-option
locks. Read locks are compatible with other read locks. A write lock is exclusive. A write-option lock is also
exclusive, prevents other views from obtaining a write lock, but still permits other views to obtain read locks;
it is useful for reserving the right to write later without blocking readers immediately.
The same types are available through CDOView.lockObjects(java.util.Collection, LockType, long) and through
the object-specific CDOObject.cdoReadLock(), CDOObject.cdoWriteLock(), and
CDOObject.cdoWriteOption() handles.
A read lock coordinates readers with writers. Several views can hold a read lock on the same object, while a write lock cannot be acquired until the incompatible read locks have been released.
A write lock is exclusive. Acquire it before a modification when the application must prevent other views from changing the object or acquiring a conflicting lock during the business operation.
A write-option lock reserves a future write. It excludes other write locks but not read locks, so it can be used while preparing an edit without making the object unreadable to other views.
CDOView.lockObjects(java.util.Collection, LockType, long) acquires all requested locks through the
repository and waits up to the supplied timeout, in milliseconds. The call is interruptible and reports an
interrupted acquisition through InterruptedException; a timed-out object-level acquisition through
java.util.concurrent.TimeoutException. The CDOView.unlockObjects(java.util.Collection, LockType)
and CDOView.unlockObjects() methods release selected or all locks owned by the view.
The object-specific CDOLock handles provide the same operations for one object, including
tryLock, CDOLock.isLocked(), and
CDOLock.isLockedByOthers(). The newer CDOLock.acquire(long, TimeUnit, boolean) form returns an
acquired-lock object that can be closed, which is convenient for a bounded scope. Always release locks in a
finally block (or close the acquired handle), including when the business operation fails.
In current CDO terminology, ordinary transaction editing is optimistic: the transaction does not acquire an
explicit write lock for every object before changing it. It works against its local revisions, receives remote
invalidations, and checks the required implicit locks and revision state during CDOTransaction.commit().
Concurrent changes can therefore produce a conflict or an org.eclipse.emf.cdo.util.OptimisticLockingException
rather than blocking the editor at the time of the first modification.
CDOTransaction.Options.getOptimisticLockingTimeout() controls how long commit waits for the implicit lock
acquisition used by that commit. It is not an explicit object-lock API. See
Committing Changes and
Transaction Options for commit and conflict handling.
Explicit, or pessimistic, locking is appropriate when a user workflow must reserve an object before doing work: for example, when an editor must not allow another editor to change the same object while a multi-step operation is in progress. Acquire a write lock before modifying, or a read lock when the workflow must exclude writers while it examines a stable state. Use a write-option lock when readers may continue but the next write must be reserved.
Lock acquisition is repository-mediated and can contend with other sessions. Use finite timeouts, handle interruption and timeout as normal control flow, lock objects in a consistent order when acquiring several, and release as soon as the protected operation ends. An explicit lock reduces a particular class of concurrent changes; it does not remove the need to handle commit failures or permissions.
CDOLockState represents all known locks for one object. It exposes the set of read-lock owners and the
single write and write-option owners through CDOLockState.getReadLockOwners(),
CDOLockState.getWriteLockOwner(), and CDOLockState.getWriteOptionOwner(). A
CDOLockOwner identifies the owning session and view and also reports the durable-locking ID and whether
the owner is currently a purely durable (not locally open) view.
CDOObject.cdoLockState() is convenient for a loaded object. A view can inspect multiple states with
CDOView.getLockStatesOfObjects(java.util.Collection) or CDOView.getLockStates(java.util.Collection).
These are client-side, currently known states; use CDOView.refreshLockStates(java.util.function.Consumer)
when an application needs the latest states from the repository before making a decision.
Durable locking persists the information needed to reopen a view, including its branch point, view kind, user identity, and locks acquired while durable locking is enabled. The durable owner is identified by a durable-locking ID rather than by the lifetime of one connected session. Consequently, closing a durable view or losing the client connection can leave its locks represented by a purely durable view in the repository.
Call CDOView.enableDurableLocking() on a view or transaction and retain the returned ID. Reopen the same
view with CDOSession.openView(String) or CDOSession.openTransaction(String). A repository/store must
support durable locking; otherwise enabling it fails. An unknown ID cannot be reopened. To end the durable lock
area, call CDOView.disableDurableLocking(boolean); passing true also releases its locks, while
false removes durability without asking the view to release them.
Durable locking is useful for reconnectable editors and long-running ownership that must survive a normal client disconnect. It is not a lease or an automatic conflict resolver: applications still need a recovery policy and must explicitly release or reclaim the durable lock area when the workflow is finished.
A view can fire CDOViewLocksChangedEvent when lock changes from other views are received. A session can
fire CDOSessionLocksChangedEvent for remote repository notifications. To receive them, configure the
session's lock notification mode to
ALWAYS, or use IF_REQUIRED_BY_VIEWS together with view notification
enablement. OFF disables delivery.
The events implement org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo; applications can inspect the
authoring lock owner, operations and lock types, affected IDs, deltas, and resulting lock states. The sender is
non-null for a local sender and null when the change came from a remote view.
Because a transaction is a view, its explicit locks are owned by that transaction's view. By default,
CDOTransaction.Options.isAutoReleaseLocksEnabled() is true, so commit and root-transaction
rollback release its locks. Set CDOTransaction.Options.setAutoReleaseLocksEnabled(boolean) to
false, or configure exemptions, when locks must survive those operations. Closing an ordinary view or
transaction releases its ordinary locks; a durable view is the deliberate exception because its lock area can
remain after the client-side view closes.
A nested transaction scope shares the root transaction's view, session, cache, dirty state, and locks. Completing or rolling back a scope does not create a repository commit and does not create a separate lock owner. See Nested Transaction Scopes for the scope lifecycle.
Ordinary locks are tied to the connected view and are released when that view is closed or its session is lost.
Applications that need to reconnect and reclaim locks must enable durable locking first, retain the returned
durable-locking ID, and reopen the view or transaction with that ID. On recovery, inspect the resulting
lock states and handle an unavailable or already-active durable view according to the
application policy. Durable state is repository-managed, so a repository that does not provide durable locking
cannot provide this recovery behavior.
Locks serialize work at the repository and create contention visible to other sessions. Prefer normal optimistic transaction processing when conflicts are rare and recoverable. When explicit locking is justified, lock the smallest useful set of objects, choose read locks instead of write locks when readers only need stability, use option locks for a genuine write reservation, keep lock duration short, and always bound acquisition waits.
Do not confuse the view's local critical section with repository locking: the former
coordinates threads using one view, while the latter coordinates views and sessions through the repository.
Likewise, lock notifications are observations of lock-state changes, not a replacement for checking the current
state before acting.