Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Missing write and read lock requests in community RWLock
When last active resource owner release lock and detect waiting thread (reader or writer) it removes LockElement from a lock, and interrupts waiting thread. That will cause totalReadCount or totalWriteCount to be out of sync with LockElement counters (because of LockElement was removed) as soon as waiting thread will take a resource. This commit should fix a problem when missing write/read lock for a popular resource cause all threads to wait for a resource to be released. Test cases for missing write and read cases provided Introduce graceful shutdown to community client, now we will wait for all active clients to complete their current requests before closing to prevent a race between closing and locks acquisition
- Loading branch information
1 parent
3ae4c7e
commit cdfce78
Showing
12 changed files
with
1,442 additions
and
224 deletions.
There are no files selected for viewing
32 changes: 32 additions & 0 deletions
32
.../kernel/src/main/java/org/neo4j/kernel/impl/locking/LockClientAlreadyClosedException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
/* | ||
* Copyright (c) 2002-2015 "Neo Technology," | ||
* Network Engine for Objects in Lund AB [http://neotechnology.com] | ||
* | ||
* This file is part of Neo4j. | ||
* | ||
* Neo4j is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
package org.neo4j.kernel.impl.locking; | ||
|
||
/** | ||
* Exception that will be thrown in case when closed {@link org.neo4j.kernel.impl.locking.Locks.Client} | ||
* will be used to acquire shared/exclusive lock | ||
*/ | ||
public class LockClientAlreadyClosedException extends RuntimeException | ||
{ | ||
public LockClientAlreadyClosedException( String message ) | ||
{ | ||
super( message ); | ||
} | ||
} |
134 changes: 134 additions & 0 deletions
134
community/kernel/src/main/java/org/neo4j/kernel/impl/locking/LockClientStateHolder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
/* | ||
* Copyright (c) 2002-2015 "Neo Technology," | ||
* Network Engine for Objects in Lund AB [http://neotechnology.com] | ||
* | ||
* This file is part of Neo4j. | ||
* | ||
* Neo4j is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
package org.neo4j.kernel.impl.locking; | ||
|
||
import java.util.concurrent.atomic.AtomicInteger; | ||
|
||
/** | ||
* State control class for Locks.Clients. | ||
* Client state represent current Locks.Client state: OPEN/CLOSED | ||
* and number of active clients | ||
*/ | ||
public final class LockClientStateHolder | ||
{ | ||
private static final int FLAG_BITS = 1; | ||
private static final int CLIENT_BITS = Integer.SIZE - FLAG_BITS; | ||
private static final int STATE_BIT_MASK = 1 << CLIENT_BITS; | ||
private static final int CLOSED = 1 << CLIENT_BITS; | ||
private static final int INITIAL_STATE = 0; | ||
private AtomicInteger clientState = new AtomicInteger( INITIAL_STATE ); | ||
|
||
/** | ||
* Check if we still have any active client | ||
* @return true if have any open client, false otherwise. | ||
*/ | ||
public boolean hasActiveClients() | ||
{ | ||
return getActiveClients( clientState.get() ) > 0; | ||
} | ||
|
||
/** | ||
* Closing current client | ||
*/ | ||
public void closeClient() | ||
{ | ||
int currentValue; | ||
do | ||
{ | ||
currentValue = clientState.get(); | ||
} | ||
while ( !clientState.compareAndSet( currentValue, stateWithNewStatus( currentValue, CLOSED ) ) ); | ||
} | ||
|
||
/** | ||
* Increment active number of clients that use current state instance. | ||
* @return false if already closed and not possible to increment active clients counter, true in case if counter | ||
* was successfully incremented. | ||
*/ | ||
public boolean incrementActiveClients() | ||
{ | ||
int currentState; | ||
do | ||
{ | ||
currentState = clientState.get(); | ||
if ( isClosed( currentState ) ) | ||
{ | ||
return false; | ||
} | ||
} | ||
while ( !clientState.compareAndSet( currentState, statusWithUpdatedClients( currentState, 1 ) ) ); | ||
return true; | ||
} | ||
|
||
/** | ||
* Decrement number of active clients that use current client state object. | ||
*/ | ||
public void decrementActiveClients() | ||
{ | ||
int currentState; | ||
do | ||
{ | ||
currentState = clientState.get(); | ||
} | ||
while ( !clientState.compareAndSet( currentState, statusWithUpdatedClients( currentState, -1 ) ) ); | ||
} | ||
|
||
/** | ||
* Check if closed | ||
* @return true if client is closed, false otherwise | ||
*/ | ||
public boolean isClosed() | ||
{ | ||
return isClosed( clientState.get() ); | ||
} | ||
|
||
/** | ||
* Reset state to initial state disregard any current state or number of active clients | ||
*/ | ||
public void reset() | ||
{ | ||
clientState.set( INITIAL_STATE ); | ||
} | ||
|
||
private boolean isClosed( int clientState ) | ||
{ | ||
return getStatus( clientState ) == CLOSED; | ||
} | ||
|
||
private int getStatus( int clientState ) | ||
{ | ||
return clientState & STATE_BIT_MASK; | ||
} | ||
|
||
private int getActiveClients( int clientState ) | ||
{ | ||
return clientState & ~STATE_BIT_MASK; | ||
} | ||
|
||
private int stateWithNewStatus( int clientState, int newStatus ) | ||
{ | ||
return newStatus | getActiveClients( clientState ); | ||
} | ||
|
||
private int statusWithUpdatedClients( int clientState, int delta ) | ||
{ | ||
return getStatus( clientState ) | (getActiveClients( clientState ) + delta); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.