Skip to content

Commit

Permalink
Add scheduled executor to enforce deadlines, make packager closable
Browse files Browse the repository at this point in the history
  • Loading branch information
skjolber committed Jul 23, 2024
1 parent 8d6ad0d commit 411e159
Show file tree
Hide file tree
Showing 37 changed files with 1,259 additions and 1,149 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package com.github.skjolber.packing.api;

import java.io.Closeable;

/**
* Fit boxes into container, i.e. perform bin packing to a single container.
*
* Thread-safe implementation.
*/

public interface Packager<B extends PackagerResultBuilder<B>> {
public interface Packager<B extends PackagerResultBuilder<B>> extends Closeable {

B newResultBuilder();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,34 @@
package com.github.skjolber.packing.deadline;

public class DeadlineCheckPackagerInterruptSupplier implements PackagerInterruptSupplier {
import java.io.Closeable;
import java.util.concurrent.ScheduledFuture;

protected final long deadline;
public class DeadlineCheckPackagerInterruptSupplier implements PackagerInterruptSupplier, Runnable, Closeable {

public DeadlineCheckPackagerInterruptSupplier(long deadline) {
super();
this.deadline = deadline;
// this is not entirely accurate for multi-threading, but close enough
// (should have been volatile)
protected boolean expired = false;
protected ScheduledFuture<?> future;

public DeadlineCheckPackagerInterruptSupplier() {
}

@Override
public boolean getAsBoolean() {
return System.currentTimeMillis() > deadline;
return expired;
}

@Override
public void run() {
this.expired = true;
}

public void close() {
future.cancel(true);
}

public void setFuture(ScheduledFuture<?> future) {
this.future = future;
}

}
Original file line number Diff line number Diff line change
@@ -1,21 +1,38 @@
package com.github.skjolber.packing.deadline;

import java.io.Closeable;
import java.util.concurrent.ScheduledFuture;
import java.util.function.BooleanSupplier;

public class DelegateDeadlineCheckPackagerInterruptSupplier implements PackagerInterruptSupplier {
public class DelegateDeadlineCheckPackagerInterruptSupplier implements PackagerInterruptSupplier, Runnable, Closeable {

// this is not entirely accurate for multi-threading, but close enough
// (should have been volatile)
protected boolean expired = false;
protected ScheduledFuture<?> future;
protected final BooleanSupplier delegate;
protected final long deadline;

public DelegateDeadlineCheckPackagerInterruptSupplier(long deadline, BooleanSupplier delegate) {

public DelegateDeadlineCheckPackagerInterruptSupplier(BooleanSupplier delegate) {
super();
this.deadline = deadline;
this.delegate = delegate;
}

@Override
public boolean getAsBoolean() {
return delegate.getAsBoolean() || System.currentTimeMillis() > deadline;
return expired || delegate.getAsBoolean();
}

@Override
public void run() {
this.expired = true;
}

public void close() {
future.cancel(true);
}

public void setFuture(ScheduledFuture<?> future) {
this.future = future;
}

}

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package com.github.skjolber.packing.deadline;

import java.io.Closeable;

@FunctionalInterface
public interface PackagerInterruptSupplier {
public interface PackagerInterruptSupplier extends Closeable {

/**
* Gets a result.
*
* @return a result
*/
boolean getAsBoolean();

default void close() {
}
}
Original file line number Diff line number Diff line change
@@ -1,49 +1,61 @@
package com.github.skjolber.packing.deadline;

import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.BooleanSupplier;

public class PackagerInterruptSupplierBuilder {

public static final NegativePackagerInterruptSupplier NOOP = new NegativePackagerInterruptSupplier();

private long deadline = Long.MAX_VALUE;
private int checkpointsPerDeadlineCheck = 1;
private BooleanSupplier interrupt = null;
private ScheduledThreadPoolExecutor scheduledThreadPoolExecutor;

public static PackagerInterruptSupplierBuilder builder() {
return new PackagerInterruptSupplierBuilder();
}

public PackagerInterruptSupplierBuilder withDeadline(long deadline, int checkpointsPerDeadlineCheck) {
public PackagerInterruptSupplierBuilder withDeadline(long deadline) {
this.deadline = deadline;
this.checkpointsPerDeadlineCheck = checkpointsPerDeadlineCheck;
return this;
}

public PackagerInterruptSupplierBuilder withInterrupt(BooleanSupplier interrupt) {
this.interrupt = interrupt;
return this;
}

public PackagerInterruptSupplierBuilder withScheduledThreadPoolExecutor(ScheduledThreadPoolExecutor executor) {
this.scheduledThreadPoolExecutor = executor;
return this;
}

public PackagerInterruptSupplier build() {
if(checkpointsPerDeadlineCheck == Integer.MAX_VALUE || checkpointsPerDeadlineCheck == -1 || deadline == Long.MAX_VALUE || deadline == -1L) {
if(deadline == Long.MAX_VALUE || deadline == -1L) {
// no deadline
if(interrupt != null) {
return new DefaultPackagerInterrupt(interrupt);
}
return NOOP;
}

if(checkpointsPerDeadlineCheck == 1) {
if(interrupt == null) {
return new DeadlineCheckPackagerInterruptSupplier(deadline);
}
return new DelegateDeadlineCheckPackagerInterruptSupplier(deadline, interrupt);

if(scheduledThreadPoolExecutor == null) {
throw new IllegalStateException("Expected scheduler");
}

if(interrupt == null) {
return new NthDeadlineCheckPackagerInterruptSupplier(deadline, checkpointsPerDeadlineCheck);
DeadlineCheckPackagerInterruptSupplier supplier = new DeadlineCheckPackagerInterruptSupplier();
ScheduledFuture<?> schedule = scheduledThreadPoolExecutor.schedule(supplier, deadline - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
supplier.setFuture(schedule);
return supplier;
}
return new DelegateNthDeadlineCheckPackagerInterruptSupplier(deadline, checkpointsPerDeadlineCheck, interrupt);

DelegateDeadlineCheckPackagerInterruptSupplier supplier = new DelegateDeadlineCheckPackagerInterruptSupplier(interrupt);
ScheduledFuture<?> schedule = scheduledThreadPoolExecutor.schedule(supplier, deadline - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
supplier.setFuture(schedule);
return supplier;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public interface PermutationRotationIterator {
/**
* Get current permutations
*
* @return
* @return current permutations array
*/

int[] getPermutations();
Expand All @@ -49,7 +49,7 @@ public interface PermutationRotationIterator {
*
* Get current length
*
* @return
* @return current length of permutations array
*/

int length();
Expand Down Expand Up @@ -112,7 +112,7 @@ public interface PermutationRotationIterator {
*
* @param state previously saved state
* @param length number of items
* @return
* @return current permutations + rotations
*/

List<PermutationRotation> get(PermutationRotationState state, int length);
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.Collections;
import java.util.List;
import java.util.concurrent.ScheduledThreadPoolExecutor;

import com.github.skjolber.packing.api.Container;
import com.github.skjolber.packing.api.ContainerItem;
Expand All @@ -27,18 +28,16 @@ public abstract class AbstractPackager<P extends PackResult, B extends PackagerR
protected static final EmptyPackResult EMPTY_PACK_RESULT = EmptyPackResult.EMPTY;

protected final PackResultComparator packResultComparator;

/** limit the number of calls to get System.currentTimeMillis() */
protected final int checkpointsPerDeadlineCheck;

protected final ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(Integer.MAX_VALUE);

/**
* Constructor
*
* @param checkpointsPerDeadlineCheck number of deadline checks to skip, before checking again
* @param packResultComparator result comparator
*/

public AbstractPackager(int checkpointsPerDeadlineCheck, PackResultComparator packResultComparator) {
this.checkpointsPerDeadlineCheck = checkpointsPerDeadlineCheck;
public AbstractPackager(PackResultComparator packResultComparator) {
this.packResultComparator = packResultComparator;
}

Expand Down Expand Up @@ -143,6 +142,7 @@ public List<Container> packList(List<StackableItem> products, List<ContainerItem
* Return a list of containers which holds all the boxes in the argument
*
* @param boxes list of boxes to fit in a container
* @param containerItems list of containers available for use in this operation
* @param limit maximum number of containers
* @param interrupt When true, the computation is interrupted as soon as possible.
* @return list of containers, or null if the deadline was reached, or empty list if the packages could not be packaged within the available containers and/or limit.
Expand Down Expand Up @@ -283,5 +283,9 @@ protected long getMinStackableArea(List<Stackable> stackables) {
}
return minArea;
}


public void close() {
scheduledThreadPoolExecutor.shutdownNow();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
@SuppressWarnings({ "rawtypes", "unchecked" })
public abstract class AbstractPackagerBuilder<P extends Packager, B extends AbstractPackagerBuilder<P, B>> {

protected int checkpointsPerDeadlineCheck = 1;
protected PackResultComparator packResultComparator;

public B withPackResultComparator(PackResultComparator packResultComparator) {
Expand All @@ -20,11 +19,6 @@ public B withPackResultComparator(PackResultComparator packResultComparator) {
return (B)this;
}

public B withCheckpointsPerDeadlineCheck(int n) {
this.checkpointsPerDeadlineCheck = n;
return (B)this;
}

public abstract P build();

}
Loading

0 comments on commit 411e159

Please sign in to comment.