Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
292 changes: 292 additions & 0 deletions blog/2026-10-11-async-await.md

Large diffs are not rendered by default.

64 changes: 64 additions & 0 deletions blog/src/AsyncUserInput.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
target C {
keepalive: true
}

reactor UserInput {
preamble {=
#include <stdio.h>
#include <stdlib.h>

// Read a line from stdin into dynamically allocated memory, dropping the
// trailing newline. The caller must free the result. Returns 0 on EOF or error.
size_t read_string(char** result) {
char* buffer = NULL;
size_t capacity = 0;
ssize_t nread = getline(&buffer, &capacity, stdin);
if (nread < 0) {
free(buffer);
return 0;
}
if (nread > 0 && buffer[nread - 1] == '\n') {
buffer[nread - 1] = '\0';
}
*result = buffer;
return nread;
}
=}
timer t(0, 5 s)
output out: char*
state count: int
physical action awake

// This reaction must precede the reaction that calls lf_async on the physical action.
// It causes that reaction to resume at the logical time of the physical action.
reaction(awake) {=
lf_async_resume(awake);
=}

reaction(startup) -> out, awake {=
while (true) {
char* line = NULL;
int nread = lf_async(awake, read_string, &line);
if (nread == 0) {
lf_request_stop();
break;
}
// The logical time is now the physical time at which the line was read.
lf_set(out, line);
}
=}

reaction(t) -> out {=
char* prompt = NULL;
asprintf(&prompt, "%d: Waiting for input...", self->count++);
lf_set(out, prompt);
=}
}

main reactor {
user_input = new UserInput()

reaction(user_input.out) {=
lf_print("At logical time " PRINTF_TIME ", Line: %s", lf_time_logical_elapsed(), user_input.out->value);
=}
}
40 changes: 40 additions & 0 deletions blog/src/AwaitLET.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
target C {
timeout: 1 s
}

reactor RequestWork {
input result: int
output request: int
timer t(0, 200 ms)
state count: int = 1

// This reaction must precede the reaction that awaits the result.
// It causes that reaction to resume at the logical time of the result.
reaction(result) {=
lf_await_resume(result);
=}

reaction(t) result -> request {=
lf_print("Request work at logical time " PRINTF_TIME, lf_time_logical_elapsed());
lf_set(request, self->count++);
lf_await(result);
lf_print("Result %d received at logical time " PRINTF_TIME, result->value, lf_time_logical_elapsed());
=}
}

reactor DoWork {
input request: int
output result: int

reaction(request) -> result {=
lf_print("Do work starting at logical time " PRINTF_TIME, lf_time_logical_elapsed());
lf_set(result, request->value * 2);
=}
}

main reactor {
request_work = new RequestWork()
process_work = new DoWork()
request_work.request -> process_work.request
process_work.result -> request_work.result after 100 ms
}
48 changes: 48 additions & 0 deletions blog/src/py/AsyncUserInput.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
target Python {
keepalive: true
}

preamble {=
# Read a line from the keyboard. Return None on EOF.
def read_string():
try:
return input()
except EOFError:
return None
=}

reactor UserInput {
timer t(0, 5 s)
output out
state count = 0
physical action awake

# This reaction must precede the reaction that calls lf_async on the physical action.
# It causes that reaction to resume at the logical time of the physical action.
reaction(awake) {=
self.lf_async_resume(awake)
=}

reaction(startup) -> out, awake {=
while True:
line = self.lf_async(awake, read_string)
if line is None:
request_stop()
break
# The logical time is now the physical time at which the line was read.
out.set(line)
=}

reaction(t) -> out {=
out.set(f"{self.count}: Waiting for input...")
self.count += 1
=}
}

main reactor {
user_input = new UserInput()

reaction(user_input.out) {=
print(f"At logical time {lf.time.logical_elapsed()}, Line: {user_input.out.value}")
=}
}
41 changes: 41 additions & 0 deletions blog/src/py/AwaitLET.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
target Python {
timeout: 1 s
}

reactor RequestWork {
input result
output request
timer t(0, 200 ms)
state count = 1

# This reaction must precede the reaction that awaits the result.
# It causes that reaction to resume at the logical time of the result.
reaction(result) {=
self.lf_await_resume(result)
=}

reaction(t) result -> request {=
print(f"Request work at logical time {lf.time.logical_elapsed()}")
request.set(self.count)
self.count += 1
self.lf_await(result)
print(f"Result {result.value} received at logical time {lf.time.logical_elapsed()}")
=}
}

reactor DoWork {
input request
output result

reaction(request) -> result {=
print(f"Do work starting at logical time {lf.time.logical_elapsed()}")
result.set(request.value * 2)
=}
}

main reactor {
request_work = new RequestWork()
process_work = new DoWork()
request_work.request -> process_work.request
process_work.result -> request_work.result after 100 ms
}
64 changes: 64 additions & 0 deletions docs/assets/code/c/src/AsyncUserInput.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
target C {
keepalive: true
}

reactor UserInput {
preamble {=
#include <stdio.h>
#include <stdlib.h>

// Read a line from stdin into dynamically allocated memory, dropping the
// trailing newline. The caller must free the result. Returns 0 on EOF or error.
size_t read_string(char** result) {
char* buffer = NULL;
size_t capacity = 0;
ssize_t nread = getline(&buffer, &capacity, stdin);
if (nread < 0) {
free(buffer);
return 0;
}
if (nread > 0 && buffer[nread - 1] == '\n') {
buffer[nread - 1] = '\0';
}
*result = buffer;
return nread;
}
=}
timer t(0, 5 s)
output out: char*
state count: int
physical action awake

// This reaction must precede the reaction that calls lf_async on the physical action.
// It causes that reaction to resume at the logical time of the physical action.
reaction(awake) {=
lf_async_resume(awake);
=}

reaction(startup) -> out, awake {=
while (true) {
char* line = NULL;
int nread = lf_async(awake, read_string, &line);
if (nread == 0) {
lf_request_stop();
break;
}
// The logical time is now the physical time at which the line was read.
lf_set(out, line);
}
=}

reaction(t) -> out {=
char* prompt = NULL;
asprintf(&prompt, "%d: Waiting for input...", self->count++);
lf_set(out, prompt);
=}
}

main reactor {
user_input = new UserInput()

reaction(user_input.out) {=
lf_print("At logical time " PRINTF_TIME ", Line: %s", lf_time_logical_elapsed(), user_input.out->value);
=}
}
40 changes: 40 additions & 0 deletions docs/assets/code/c/src/AwaitLET.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
target C {
timeout: 1 s
}

reactor RequestWork {
input result: int
output request: int
timer t(0, 200 ms)
state count: int = 1

// This reaction must precede the reaction that awaits the result.
// It causes that reaction to resume at the logical time of the result.
reaction(result) {=
lf_await_resume(result);
=}

reaction(t) result -> request {=
lf_print("Request work at logical time " PRINTF_TIME, lf_time_logical_elapsed());
lf_set(request, self->count++);
lf_await(result);
lf_print("Result %d received at logical time " PRINTF_TIME, result->value, lf_time_logical_elapsed());
=}
}

reactor DoWork {
input request: int
output result: int

reaction(request) -> result {=
lf_print("Do work starting at logical time " PRINTF_TIME, lf_time_logical_elapsed());
lf_set(result, request->value * 2);
=}
}

main reactor {
request_work = new RequestWork()
process_work = new DoWork()
request_work.request -> process_work.request
process_work.result -> request_work.result after 100 ms
}
48 changes: 48 additions & 0 deletions docs/assets/code/py/src/AsyncUserInput.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
target Python {
keepalive: true
}

preamble {=
# Read a line from the keyboard. Return None on EOF.
def read_string():
try:
return input()
except EOFError:
return None
=}

reactor UserInput {
timer t(0, 5 s)
output out
state count = 0
physical action awake

# This reaction must precede the reaction that calls lf_async on the physical action.
# It causes that reaction to resume at the logical time of the physical action.
reaction(awake) {=
self.lf_async_resume(awake)
=}

reaction(startup) -> out, awake {=
while True:
line = self.lf_async(awake, read_string)
if line is None:
request_stop()
break
# The logical time is now the physical time at which the line was read.
out.set(line)
=}

reaction(t) -> out {=
out.set(f"{self.count}: Waiting for input...")
self.count += 1
=}
}

main reactor {
user_input = new UserInput()

reaction(user_input.out) {=
print(f"At logical time {lf.time.logical_elapsed()}, Line: {user_input.out.value}")
=}
}
41 changes: 41 additions & 0 deletions docs/assets/code/py/src/AwaitLET.lf
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
target Python {
timeout: 1 s
}

reactor RequestWork {
input result
output request
timer t(0, 200 ms)
state count = 1

# This reaction must precede the reaction that awaits the result.
# It causes that reaction to resume at the logical time of the result.
reaction(result) {=
self.lf_await_resume(result)
=}

reaction(t) result -> request {=
print(f"Request work at logical time {lf.time.logical_elapsed()}")
request.set(self.count)
self.count += 1
self.lf_await(result)
print(f"Result {result.value} received at logical time {lf.time.logical_elapsed()}")
=}
}

reactor DoWork {
input request
output result

reaction(request) -> result {=
print(f"Do work starting at logical time {lf.time.logical_elapsed()}")
result.set(request.value * 2)
=}
}

main reactor {
request_work = new RequestWork()
process_work = new DoWork()
request_work.request -> process_work.request
process_work.result -> request_work.result after 100 ms
}
1 change: 1 addition & 0 deletions docs/assets/images/diagrams/AsyncUserInput.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/assets/images/diagrams/AwaitLET.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading