Skip to content
Merged
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
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1123,6 +1123,7 @@ MEMCHECK_CASES := \
tests/cases/test_list_transform_ownership \
tests/cases/test_result_unwrap_or_fallback_leak \
tests/cases/test_borrowed_operand_extraction \
tests/cases/test_task_result_payloads \
tests/cases/test_struct_closure_field tests/cases/test_generic_struct_field_dtor \
tests/cases/test_iterator_drain tests/cases/test_method_fresh_string_return \
tests/cases/test_lazy_adapter_pipeline tests/cases/test_named_local_adapter_chain \
Expand Down
22 changes: 9 additions & 13 deletions bench/grpc/pith/client.pith
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,16 @@ fn echo_request(payload: Bytes) -> Bytes:
w.write_bytes(1, payload) catch false
return w.bytes()

# time each call, pushing the latency of every call into `lat` (a shared list
# handle created by the caller — task results with list payloads are avoided on
# purpose, the handle is the simpler contract).
fn timed_batch(ch: grpc.Conn, method: String, request: Bytes, count: Int, lat: List[Int]) -> Int:
# time each call and return the latency of every call.
fn timed_batch(ch: grpc.Conn, method: String, request: Bytes, count: Int) -> List[Int]:
mut lat := []
mut i := 0
while i < count:
t0 := time.mono_nanos()
ch.unary(method, request) catch bytes.empty()
lat.push(time.mono_nanos() - t0)
i = i + 1
return count
return lat

# each worker gets its OWN connection from the pool (round-robin) and keeps its
# calls on that one connection's pipeline, so the pool spreads load across
Expand All @@ -77,18 +76,14 @@ fn timed_batch(ch: grpc.Conn, method: String, request: Bytes, count: Int, lat: L
fn run_concurrent(pool: grpc.PoolConn, method: String, request: Bytes, calls: Int, conc: Int, merged: List[Int]):
per := calls / conc
mut tasks := []
mut buckets: List[List[Int]] := []
mut w := 0
while w < conc:
c := pool.at(w % pool.size())
bucket: List[Int] := []
buckets.push(bucket)
tasks.push(spawn timed_batch(c, method, request, per, bucket))
tasks.push(spawn timed_batch(c, method, request, per))
w = w + 1
for t in tasks:
await t
for bucket in buckets:
for v in bucket:
lat := await t
for v in lat:
merged.push(v)

# the value at `pct` percent of the way through a sorted list, matching the
Expand Down Expand Up @@ -160,7 +155,8 @@ fn main():
start := time.now()
latencies: List[Int] := []
if conc <= 1:
timed_batch(ch.at(0), method, request, calls, latencies)
for v in timed_batch(ch.at(0), method, request, calls):
latencies.push(v)
else:
run_concurrent(ch, method, request, calls, conc, latencies)
elapsed := time.now() - start
Expand Down
6 changes: 6 additions & 0 deletions docs/idiomatic_pith.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ few elements, switch to the lazy adapters in `std.iter`. The chain
fuses and the source is walked once. See `docs/iterators.md` for the
protocol and worked examples.

An empty literal takes its element type from an annotation when you give one
(`mut names: List[String] := []`), and otherwise from the first value stored
into it — `mut tasks := []` followed by `tasks.push(spawn worker())` types
`tasks` as a list of tasks. Annotate when the first store sits far from the
declaration and a reader would have to hunt for it.

Collections are shared handles. If a function needs to mutate its own top-level
container, start with `copy_list`, `copy_map`, or `copy_set`.

Expand Down
10 changes: 10 additions & 0 deletions examples/concurrency.pith
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ fn main() -> Int!:
value := await spawn compute(21)
print(value.to_string())

# fan out over a list of tasks; the list needs no annotation, the
# first push names its element type
mut work := []
for n in 1..=3:
work.push(spawn compute(n))
mut doubled := []
for t in work:
doubled.push(await t)
print("doubled: {doubled[0]} {doubled[1]} {doubled[2]}")

jobs := Channel[Int](1)
jobs.send(7)
picked := select:
Expand Down
1 change: 1 addition & 0 deletions examples/expected/concurrency.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
response from https://example.com
42
doubled: 2 4 6
select picked: 7
await_ctx timed out: true
send_ctx ok: true
Expand Down
Loading
Loading