Last active
January 28, 2025 17:16
-
-
Save jeffque/1ae97c285cf35b13edeb4612a2911b87 to your computer and use it in GitHub Desktop.
A controller that yields a stream of results while computing Ackerman Peter function to check timeout problems in Spring Boot
This file contains hidden or 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
@RestController | |
class SlowlorrisController { | |
@GetMapping(path = "/slowlorris2", produces = "text/plain") | |
StreamingResponseBody teste2() { | |
return out -> { | |
out.write(("COMEÇANDO!!!\n").getBytes(Charset.defaultCharset())); | |
out.flush(); | |
for (int i = 0; i < 1000; i++) { | |
out.write(("rodada %d\n".formatted(i)).getBytes(Charset.defaultCharset())); | |
System.out.println("passei aqui antes do ack peter"); | |
final var result = ackPeter(3, 3, out); | |
System.out.println("passei aqui depois do ack peter"); | |
out.write(("\tresult %d\n".formatted(result)).getBytes(Charset.defaultCharset())); | |
out.flush(); | |
} | |
out.write(("FINISHED\n").getBytes(Charset.defaultCharset())); | |
}; | |
} | |
@GetMapping(path = "/slowlorris", produces = "text/plain") | |
ResponseEntity<StreamingResponseBody> teste() { | |
return ResponseEntity | |
.ok() | |
.body(out -> { | |
out.write(("COMEÇANDO!!!\n").getBytes(Charset.defaultCharset())); | |
out.flush(); | |
for (int i = 0; i < 1000; i++) { | |
out.write(("rodada %d\n".formatted(i)).getBytes(Charset.defaultCharset())); | |
System.out.println("passei aqui antes do ack peter"); | |
final var result = ackPeter(3, 3, out); | |
System.out.println("passei aqui depois do ack peter"); | |
out.write(("\tresult %d\n".formatted(result)).getBytes(Charset.defaultCharset())); | |
out.flush(); | |
} | |
out.write(("FINISHED\n").getBytes(Charset.defaultCharset())); | |
}); | |
} | |
interface Continuation { | |
boolean finished(); | |
long value(); | |
Continuation step(); | |
// computation has over | |
static Continuation found(long v) { | |
return new Continuation() { | |
@Override | |
public boolean finished() { | |
return true; | |
} | |
@Override | |
public long value() { | |
return v; | |
} | |
@Override | |
public Continuation step() { | |
return this; | |
} | |
}; | |
} | |
// go on computing | |
static Continuation goon(Supplier<Continuation> nextStep) { | |
return new Continuation() { | |
@Override | |
public boolean finished() { | |
return false; | |
} | |
@Override | |
public long value() { | |
return 0; | |
} | |
@Override | |
public Continuation step() { | |
return nextStep.get(); | |
} | |
}; | |
} | |
} | |
private static Continuation ackermannPeter(long m, Continuation c, OutputStream out) { | |
if (!c.finished()) { | |
safewrite(out, "descendo com m %d e n continuation\n".formatted(m)); | |
return Continuation.goon(() -> { | |
final var next = c.step(); | |
return Continuation.goon(() -> ackermannPeter(m, next, out)); | |
}); | |
} | |
final var n = c.value(); | |
safewrite(out, "descendo com m %d e n %d\n".formatted(m, n)); | |
if (m <= 0) { | |
return Continuation.found(n + 1); | |
} | |
if (n <= 0) { | |
return Continuation.goon(() -> ackermannPeter(m - 1, Continuation.found(1), out)); | |
} | |
return Continuation.goon(() -> | |
ackermannPeter(m - 1, | |
Continuation.goon(() -> ackermannPeter(m, Continuation.found(n - 1), out | |
)), out) | |
); | |
} | |
private static void safewrite(OutputStream out, String s) { | |
try { | |
out.write(s.getBytes(Charset.defaultCharset())); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
throw new RuntimeException(e); | |
} | |
} | |
public static long ackPeter(long m, long n, OutputStream out) { | |
return compute(Continuation.goon(() -> ackermannPeter(m, Continuation.found(n), new FilterOutputStream(out) { | |
long startWindow = System.currentTimeMillis(); | |
int cnt = 0; | |
@Override | |
public void write(byte[] b) throws IOException { | |
super.write(b); | |
cnt++; | |
if (cnt % 8000 == 0) { | |
long now = System.currentTimeMillis(); | |
super.write(("SINCELASTCALL: %d\n".formatted(now - startWindow)).getBytes(Charset.defaultCharset())); | |
startWindow = now; | |
super.flush(); | |
cnt = 0; | |
} | |
} | |
}))); | |
} | |
static long compute(Continuation c) { | |
while (!c.finished()) { | |
c = c.step(); | |
} | |
return c.value(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Check https://computaria.gitlab.io/blog/2024/01/23/trampoline-ackermann-peter for details about the implementation of Ackermann Peter function