Skip to content

Commit

Permalink
runtime-core: Guard from multiple invocations, fix #708
Browse files Browse the repository at this point in the history
  • Loading branch information
minborg committed Jun 10, 2019
1 parent 1c29fde commit b4cc6dd
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 1 deletion.
Expand Up @@ -24,18 +24,25 @@
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
*
* @author Per Minborg
*/
public class StatisticsReporterSchedulerComponentImpl implements StatisticsReporterSchedulerComponent {

private final AtomicBoolean outstanding;

@Config(name = "statistics.scheduler.enabled", value = "true")
private boolean enabled;

private ScheduledExecutorService scheduler;

public StatisticsReporterSchedulerComponentImpl() {
outstanding = new AtomicBoolean();
}

@ExecuteBefore(State.INITIALIZED)
public void init() {
if (enabled) {
Expand All @@ -51,7 +58,7 @@ public void init() {
public void start(StatisticsReporterComponent src) {
if (enabled) {
scheduler.submit(src::reportStarted);
scheduler.scheduleAtFixedRate(src::alive, 1, 1, TimeUnit.HOURS);
scheduler.scheduleAtFixedRate(() -> guardedCall(src::alive), 1, 1, TimeUnit.HOURS);
}
}

Expand All @@ -70,4 +77,19 @@ public void stop(StatisticsReporterComponent src) {
}
}

/**
* Method to guard from multiple invocations. Used to fix #708
* @param r runnable
*/
void guardedCall(Runnable r) {
if (outstanding.compareAndSet(false,true)) {
try {
r.run();
} finally {
outstanding.set(false);
}

}
}

}
@@ -0,0 +1,50 @@
package com.speedment.runtime.core.internal.component;

import org.junit.jupiter.api.Test;

import java.util.concurrent.atomic.AtomicBoolean;

import static org.junit.jupiter.api.Assertions.*;

class StatisticsReporterSchedulerComponentImplTest {

@Test
void guardedCallStalled() {

final AtomicBoolean called = new AtomicBoolean();
final StatisticsReporterSchedulerComponentImpl instance = new StatisticsReporterSchedulerComponentImpl();

final Thread t0 = new Thread(() -> { instance.guardedCall(() -> dly(1000)); });
final Thread t1 = new Thread(() -> { called.set(true); });

t0.start();
dly(100);
t1.start();

assertFalse(called.get());
}


@Test
void guardedCallClean() {

final AtomicBoolean called = new AtomicBoolean();
final StatisticsReporterSchedulerComponentImpl instance = new StatisticsReporterSchedulerComponentImpl();

instance.guardedCall(() -> {});
instance.guardedCall(() -> called.set(true));

assertTrue(called.get());
}



private void dly(long ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException ignored) {

}
}

}

0 comments on commit b4cc6dd

Please sign in to comment.