Skip to content

Commit

Permalink
feat: set custom action names
Browse files Browse the repository at this point in the history
  • Loading branch information
stawirej committed Dec 28, 2023
1 parent 7d4c5ff commit 9c4fcd5
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 13 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,4 +183,11 @@ void Thread_safe_counter() {

```groovy
testImplementation group: 'pl.amazingcode', name: 'threads-collider', version: "1.0.2"
```
```

### TODO

- [ ] Add deadlock detection for ThreadCollider
- [ ] Update javadocs for timeout methods for ThreadsCollider
- [ ] Check negative times method arguments
- [ ] Check processors count
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
import java.lang.management.ThreadMXBean;
import java.util.concurrent.TimeUnit;

/**
* Exception thrown when {@link pl.amazingcode.threadscollider.single.ThreadsCollider} or {@link
* pl.amazingcode.threadscollider.multi.MultiThreadsCollider} has not finished threads within the
* specified timeout.
*/
public final class UnfinishedThreads extends RuntimeException {

private static final String MESSAGE =
Expand All @@ -25,6 +30,13 @@ private UnfinishedThreads(String message) {
super(message);
}

/**
* Creates new instance of {@link UnfinishedThreads}.
*
* @param timeout - threads timeout
* @param timeUnit - timeout time unit
* @return {@link UnfinishedThreads}
*/
public static UnfinishedThreads becauseTimeoutExceeded(long timeout, TimeUnit timeUnit) {

return new UnfinishedThreads(format(MESSAGE, timeout, timeUnit, threadDump()));
Expand Down
37 changes: 37 additions & 0 deletions src/main/java/pl/amazingcode/threadscollider/multi/Action.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package pl.amazingcode.threadscollider.multi;

import java.util.Optional;

class Action {

private final Runnable runnable;

@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
private final Optional<String> actionName;

private Action(Runnable runnable, Optional<String> actionName) {

this.runnable = runnable;
this.actionName = actionName;
}

static Action of(Runnable runnable, String actionName) {

return new Action(runnable, Optional.ofNullable(actionName));
}

static Action of(Runnable runnable) {

return new Action(runnable, Optional.empty());
}

Runnable runnable() {

return runnable;
}

Optional<String> actionName() {

return actionName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,13 @@ public interface MandatoryActionBuilder {
* @return {@link TimesBuilder}
*/
TimesBuilder withAction(Runnable action);

/**
* Set action to be executed.
*
* @param action action to be executed
* @param actionName description of action used when reporting deadlocked threads.
* @return {@link TimesBuilder}
*/
TimesBuilder withAction(Runnable action, String actionName);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ public interface MultiOptionalBuilder {
* same value as timeout for {@link MultiThreadsCollider#collide()}.
*
* @param timeout - await termination timeout for executor service used by {@link
* ThreadsCollider}.
* MultiThreadsCollider} and for running threads. In case of presence of running threads
* timeout, total {@link MultiThreadsCollider} timeout is sum of executor service timeout and
* running threads timeout (timeout * 2).
* @return {@link MultiTimeUnitBuilder}
*/
MultiTimeUnitBuilder withAwaitTerminationTimeout(long timeout);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
Expand All @@ -27,7 +28,7 @@ public final class MultiThreadsCollider implements AutoCloseable {
return thread;
};

private final List<Runnable> runnables;
private final List<Action> runnables;
private final List<Integer> times;
private final ExecutorService executor;
private final int threadsCount;
Expand All @@ -39,7 +40,7 @@ public final class MultiThreadsCollider implements AutoCloseable {
private final Consumer<Exception> threadsExceptionsConsumer;

private MultiThreadsCollider(
List<Runnable> runnables,
List<Action> runnables,
List<Integer> times,
int threadsCount,
long timeout,
Expand Down Expand Up @@ -67,14 +68,14 @@ private MultiThreadsCollider(
public void collide() {

try {
Iterator<Runnable> runnableIterator = runnables.iterator();
Iterator<Action> runnableIterator = runnables.iterator();
Iterator<Integer> timesIterator = times.iterator();

while (runnableIterator.hasNext()) {
Runnable runnable = runnableIterator.next();
Action action = runnableIterator.next();
int times = timesIterator.next();
for (int i = 0; i < times; i++) {
executor.execute(() -> decorate(runnable));
executor.execute(() -> decorate(action));
}
}

Expand All @@ -92,9 +93,11 @@ public void collide() {
}
}

private void decorate(Runnable runnable) {
private void decorate(Action action) {

try {
setThreadName(action.actionName());

startedThreadsCount.incrementAndGet();

while (startedThreadsCount.get() < threadsCount)
Expand All @@ -103,14 +106,22 @@ private void decorate(Runnable runnable) {
while (spinLock.get())
;

runnable.run();
action.runnable().run();
} catch (Exception exception) {
consumeException(exception);
} finally {
runningThreadsLatch.countDown();
}
}

@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
private void setThreadName(Optional<String> actionName) {

actionName
.map(name -> Thread.currentThread().getName() + " [" + name + "]")
.ifPresent(Thread.currentThread()::setName);
}

/** Shuts down the executor service and waits for all threads to finish by given timeout. */
@Override
public void close() {
Expand All @@ -137,7 +148,7 @@ public static class MultiThreadsColliderBuilder
MultiTimeUnitBuilder,
MultiOptionalBuilder {

private final List<Runnable> runnables;
private final List<Action> runnables;
private final List<Integer> times;
private long timeout = DEFAULT_TIMEOUT;
private TimeUnit timeUnit = DEFAULT_TIME_UNIT;
Expand All @@ -162,7 +173,14 @@ public static MandatoryActionBuilder multiThreadsCollider() {
@Override
public TimesBuilder withAction(Runnable runnable) {

this.runnables.add(runnable);
this.runnables.add(Action.of(runnable));
return this;
}

@Override
public TimesBuilder withAction(Runnable runnable, String actionName) {

this.runnables.add(Action.of(runnable, actionName));
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ public interface OptionalActionBuilder {
*/
TimesBuilder withAction(Runnable action);

/**
* Sets action to be executed by {@link MultiThreadsCollider}.
*
* @param action action to be executed by {@link MultiThreadsCollider}
* @param actionName description of action used when reporting deadlocked threads.
* @return {@link TimesBuilder}
*/
TimesBuilder withAction(Runnable action, String actionName);

/**
* Sets await termination timeout for executor service used by {@link MultiThreadsCollider}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayNameGeneration;
import org.junit.jupiter.api.DisplayNameGenerator;
import org.junit.jupiter.api.RepeatedTest;
Expand Down Expand Up @@ -43,6 +44,7 @@ void setUp() {
list2 = new ArrayList<>();
}

@Disabled
@RepeatedTest(10)
void Detect_deadlock() {
// Given
Expand All @@ -51,9 +53,9 @@ void Detect_deadlock() {
// When
try (MultiThreadsCollider collider =
multiThreadsCollider()
.withAction(() -> update1(list1, list2))
.withAction(() -> update1(list1, list2), "update1")
.times(ACTION_THREADS_COUNT)
.withAction(() -> update2(list2, list1))
.withAction(() -> update2(list2, list1), "update2")
.times(ACTION_THREADS_COUNT)
.withThreadsExceptionsConsumer(exceptions::add)
.withAwaitTerminationTimeout(100)
Expand Down

0 comments on commit 9c4fcd5

Please sign in to comment.