Skip to content

Commit

Permalink
Implement cancellation (eclipse-che#3701)
Browse files Browse the repository at this point in the history
  • Loading branch information
Yevhenii Voevodin authored Jan 16, 2017
1 parent 44c296f commit c16bc3f
Show file tree
Hide file tree
Showing 21 changed files with 1,799 additions and 1,191 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,24 @@ che.workspace.auto_snapshot=true
# Otherwise create a new workspace.
che.workspace.auto_restore=true


# Workspace threads pool configuration, this pool is used for workspace related
# operations that require asynchronous execution e.g. starting/stopping/snapshotting

# possible values are 'fixed', 'cached'
che.workspace.pool.type=fixed

# This property is ignored when pool type is different from 'fixed'.
# Configures the exact size of the pool, if it's set multiplier property is ignored.
# If this property is not set(0, < 0, NULL) then pool sized to number of cores,
#it can be modified within multiplier
che.workspace.pool.exact_size=NULL

# This property is ignored when pool type is different from 'fixed' or exact pool size is set.
# If it's set the pool size will be N_CORES * multiplier
che.workspace.pool.cores_multiplier=2


# Java command line options used to start Che agent in workspace runtime
che.workspace.java_opts=-Xms256m -Xmx2048m -Djava.security.egd=file:/dev/./urandom

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class CompositeLineConsumer implements LineConsumer {
private static final Logger LOG = LoggerFactory.getLogger(CompositeLineConsumer.class);

private final List<LineConsumer> lineConsumers;
private boolean isOpen;
private boolean isOpen;

public CompositeLineConsumer(LineConsumer... lineConsumers) {
this.lineConsumers = new CopyOnWriteArrayList<>(lineConsumers);
Expand Down Expand Up @@ -70,7 +70,11 @@ public void writeLine(String line) {
for (LineConsumer lineConsumer : lineConsumers) {
try {
lineConsumer.writeLine(line);
} catch (ConsumerAlreadyClosedException | ClosedByInterruptException e) {
} catch (ClosedByInterruptException interrupted) {
Thread.currentThread().interrupt();
isOpen = false;
return;
} catch (ConsumerAlreadyClosedException e) {
lineConsumers.remove(lineConsumer); // consumer is already closed, so we cannot write into it any more
if (lineConsumers.size() == 0) { // if all consumers are closed then we can close this one
isOpen = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.testng.Assert.assertTrue;

/**
* @author Mykola Morhun
Expand All @@ -49,7 +50,7 @@ public class CompositeLineConsumerTest {

@BeforeMethod
public void beforeMethod() throws Exception {
subConsumers = new LineConsumer[] { lineConsumer1, lineConsumer2, lineConsumer3 };
subConsumers = new LineConsumer[] {lineConsumer1, lineConsumer2, lineConsumer3};
compositeLineConsumer = new CompositeLineConsumer(subConsumers);
}

Expand Down Expand Up @@ -86,8 +87,7 @@ public void shouldNotWriteIntoSubConsumersAfterClosingCompositeConsumer() throws
public Object[][] subConsumersExceptions() {
return new Throwable[][] {
{new ConsumerAlreadyClosedException("Error")},
{new ClosedByInterruptException()}
};
};
}

@Test(dataProvider = "subConsumersExceptions")
Expand Down Expand Up @@ -132,7 +132,19 @@ public void shouldDoNothingOnWriteLineIfAllSubConsumersAreClosed() throws Except
}
}

private LineConsumer[] appendTo(LineConsumer[] base, LineConsumer... toAppend ) {
@Test
public void stopsWritingOnceInterrupted() throws Exception {
doThrow(new ClosedByInterruptException()).when(lineConsumer2).writeLine("test");

compositeLineConsumer.writeLine("test");

assertTrue(Thread.interrupted());
verify(lineConsumer1).writeLine("test");
verify(lineConsumer2).writeLine("test");
verify(lineConsumer3, never()).writeLine("test");
}

private LineConsumer[] appendTo(LineConsumer[] base, LineConsumer... toAppend) {
List<LineConsumer> allElements = new ArrayList<>();
allElements.addAll(Arrays.asList(base));
allElements.addAll(Arrays.asList(toAppend));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ public enum WorkspaceStatus {
* <p>Workspace becomes starting only if it was {@link #STOPPED}.
* The status map:
* <pre>
* STOPPED -> <b>STARTING</b> -> RUNNING (normal behaviour)
* STOPPED -> <b>STARTING</b> -> STOPPED (failed to start)
* STOPPED -> <b>STARTING</b> -> RUNNING (normal behaviour)
* STOPPED -> <b>STARTING</b> -> STOPPED (failed to start)
* STOPPED -> <b>STARTING</b> -> STOPPING (explicitly stopped)
* </pre>
*/
STARTING,
Expand Down Expand Up @@ -61,10 +62,12 @@ public enum WorkspaceStatus {
/**
* Workspace considered as stopping if and only if its active environment is shutting down.
*
* <p>Workspace is in stopping status only if it was in {@link #RUNNING} status before.
* <p>Workspace is in stopping status only if it was in {@link #RUNNING} or
* {@link #STARTING} status before.
* The status map:
* <pre>
* RUNNING -> <b>STOPPING</b> -> STOPPED (normal behaviour)/(error while stopping)
* RUNNING -> <b>STOPPING</b> -> STOPPED (normal behaviour)/(error while stopping)
* STARTING -> <b>STOPPING</b> -> STOPPED (stopped while starting)
* </pre>
*/
STOPPING,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,36 @@

import com.google.common.util.concurrent.Striped;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;

/**
* Helper class to use striped locks in try-with-resources construction.
* </p>
* Examples of usage:
* <pre class="code"><code class="java">
* StripedLocks stripedLocks = new StripedLocks(16);
* try (CloseableLock lock = stripedLocks.acquireWriteLock(myKey)) {
* syncedObject.write();
* }
* <pre>{@code
* StripedLocks stripedLocks = new StripedLocks(16);
* try (Unlocker u = stripedLocks.writeLock(myKey)) {
* syncedObject.write();
* }
*
* try (CloseableLock lock = stripedLocks.acquireReadLock(myKey)) {
* syncedObject.read();
* }
* try (Unlocker u = stripedLocks.readLock(myKey)) {
* syncedObject.read();
* }
*
* try (CloseableLock lock = stripedLocks.acquireWriteAllLock(myKey)) {
* for (ObjectToSync objectToSync : allObjectsToSync) {
* objectToSync.write();
* }
* }
* </pre>
* try (Unlocker u = stripedLocks.writeAllLock(myKey)) {
* for (ObjectToSync objectToSync : allObjectsToSync) {
* objectToSync.write();
* }
* }
* }</pre>
*
* @author Alexander Garagatyi
* @author Sergii Leschenko
* @author Yevhenii Voevodin
*/
// TODO consider usage of plain map with locks instead of Guava's Striped
public class StripedLocks {

private final Striped<ReadWriteLock> striped;

public StripedLocks(int stripesCount) {
Expand All @@ -49,75 +51,66 @@ public StripedLocks(int stripesCount) {
/**
* Acquire read lock for provided key.
*/
public CloseableLock acquireReadLock(String key) {
return new ReadLock(key);
public Unlocker readLock(String key) {
Lock lock = striped.get(key).readLock();
lock.lock();
return new LockUnlocker(lock);
}

/**
* Acquire write lock for provided key.
*/
public CloseableLock acquireWriteLock(String key) {
return new WriteLock(key);
public Unlocker writeLock(String key) {
Lock lock = striped.get(key).writeLock();
lock.lock();
return new LockUnlocker(lock);
}

/**
* Acquire write lock for all possible keys.
*/
public CloseableLock acquireWriteAllLock() {
return new WriteAllLock();
}

/**
* Represents read lock for the provided key.
* Can be used as {@link AutoCloseable} to release lock.
*/
private class ReadLock implements CloseableLock {
private String key;

private ReadLock(String key) {
this.key = key;
striped.get(key).readLock().lock();
public Unlocker writeAllLock() {
Lock[] locks = getAllWriteLocks();
for (Lock lock : locks) {
lock.lock();
}
return new LocksUnlocker(locks);
}

@Override
public void close() {
striped.get(key).readLock().unlock();
private Lock[] getAllWriteLocks() {
Lock[] locks = new Lock[striped.size()];
for (int i = 0; i < striped.size(); i++) {
locks[i] = striped.getAt(i).writeLock();
}
return locks;
}

/**
* Represents write lock for the provided key.
* Can be used as {@link AutoCloseable} to release lock.
*/
private class WriteLock implements CloseableLock {
private String key;
private static class LockUnlocker implements Unlocker {

private WriteLock(String key) {
this.key = key;
striped.get(key).writeLock().lock();
private final Lock lock;

private LockUnlocker(Lock lock) {
this.lock = lock;
}

@Override
public void close() {
striped.get(key).writeLock().unlock();
public void unlock() {
lock.unlock();
}
}

/**
* Represents write lock for all possible keys.
* Can be used as {@link AutoCloseable} to release locks.
*/
private class WriteAllLock implements CloseableLock {
private WriteAllLock() {
for (int i = 0; i < striped.size(); i++) {
striped.getAt(i).writeLock().lock();
}
private static class LocksUnlocker implements Unlocker {

private final Lock[] locks;

private LocksUnlocker(Lock[] locks) {
this.locks = locks;
}

@Override
public void close() {
for (int i = 0; i < striped.size(); i++) {
striped.getAt(i).writeLock().unlock();
public void unlock() {
for (Lock lock : locks) {
lock.unlock();
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,28 @@
package org.eclipse.che.commons.lang.concurrent;

/**
* Lock that is designed to use in try-with-resources statement.
* An interface that allows implementations to enclose
* locked instance and unlock it later by calling {@link #unlock()}.
*
* <p>Implementers should lock on instance creation
* and unlock when {@link CloseableLock#close()} method invokes.
* <p>This is designed to be used in try-with-resources statement.
*
* <p>The example:
* <pre>
* try (@SuppressWarnings("unused") Unlocker u = customLocks.lock("key")) {
* // do something in lock
* }
* </pre>
*
* @author Sergii Leschenko
* @author Yevhenii Voevodin
*/
public interface CloseableLock extends AutoCloseable {
public interface Unlocker extends AutoCloseable {

/**
* Unlocks this lock.
*
* This method is invoked automatically on objects managed by the
* {@code try}-with-resources statement.
* Unlocks the corresponding lock in implementation specific manner.
*/
void unlock();

@Override
void close();
default void close() { unlock(); }
}
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,7 @@ private String buildImage(final DockerConnection dockerConnection,
throw new DockerException(e.getCause().getLocalizedMessage(), 500);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new DockerException("Docker image build was interrupted", 500);
}
}
Expand Down Expand Up @@ -952,6 +953,7 @@ public String push(final PushParams params, final ProgressMonitor progressMonito
// unwrap exception thrown by task with .getCause()
throw new DockerException("Docker image pushing failed. Cause: " + e.getCause().getLocalizedMessage(), 500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new DockerException("Docker image pushing was interrupted", 500);
}
}
Expand Down Expand Up @@ -1048,6 +1050,7 @@ protected void pull(final PullParams params,
// unwrap exception thrown by task with .getCause()
throw new DockerException(e.getCause().getLocalizedMessage(), 500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new DockerException("Docker image pulling was interrupted", 500);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -471,8 +471,7 @@ protected void pullImage(CheServiceImpl service,
docker.removeImage(RemoveImageParams.create(fullNameOfPulledImage).withForce(false));
}
} catch (IOException e) {
LOG.error(e.getLocalizedMessage(), e);
throw new MachineException("Can't create machine from image. Cause: " + e.getLocalizedMessage());
throw new MachineException("Can't create machine from image. Cause: " + e.getLocalizedMessage(), e);
}
}

Expand Down
Loading

0 comments on commit c16bc3f

Please sign in to comment.