Skip to content

Commit 7b433a6

Browse files
Add support for channels
1 parent e0aeb33 commit 7b433a6

29 files changed

Lines changed: 1348 additions & 382 deletions

.vscode/settings.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"Fcomplex",
3131
"flowargs",
3232
"fsyntax",
33+
"futs",
3334
"GOARCH",
3435
"gofmt",
3536
"goimports",
@@ -105,6 +106,7 @@
105106
"varsser",
106107
"venv",
107108
"vmihailenco",
109+
"WAITGROUP",
108110
"XDECREF"
109111
],
110112
"python.defaultInterpreterPath": "./.venv/bin/python",

pkg/exports.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,16 @@ func Send(line int, id func(Context) string, value func(Context) any) AwaitBlock
7373
func Recv[T any](line int, id func(Context) string, ret func(Context, T)) AwaitBlock {
7474
return runtime.Recv[T](line, id, ret)
7575
}
76+
func RecvAny(
77+
line int,
78+
ids []func(Context) string,
79+
ref func(Context, []byte, int),
80+
) AwaitBlock {
81+
return runtime.RecvAny(line, ids, ref)
82+
}
83+
func Deser[T any](line int, data func(Context) []byte, ret func(Context, T)) SyncBlock {
84+
return runtime.Deser(line, data, ret)
85+
}
7686
func WaitGroup(
7787
line int, value func(Context) int,
7888
ret func(Context, string),

pkg/runtimes/dupher/blocks.go

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ type BodyLabeledBlock struct {
3636
}
3737
type AwaitBlock struct {
3838
line int
39-
fut Future
39+
futs []Future
4040
}
4141
type SyncBlock struct {
4242
line int
@@ -84,7 +84,7 @@ func BodyLabeled(line int, label string, stmts ...Block) BodyLabeledBlock {
8484
}
8585

8686
func Await(line int, start StartFunc, trigger TriggerFunc) AwaitBlock {
87-
return AwaitBlock{line: line, fut: Future{start: start, trigger: trigger}}
87+
return AwaitBlock{line: line, futs: []Future{{start: start, trigger: trigger}}}
8888
}
8989

9090
func Sync(line int, stmt func(Context)) SyncBlock {
@@ -294,6 +294,37 @@ func Release(line int, id func(Context) string) AwaitBlock {
294294
)
295295
}
296296

297+
func RecvAny(
298+
line int,
299+
ids []func(Context) string,
300+
ref func(Context, []byte, int),
301+
) AwaitBlock {
302+
futs := make([]Future, len(ids))
303+
for idx, id := range ids {
304+
futs[idx] = Future{
305+
start: func(ctx Context) (platforms.Event, error) {
306+
return platforms.ChannelRecvEvent{Id: id(ctx)}, nil
307+
},
308+
trigger: func(ctx Context, notify platforms.EventNotify) error {
309+
recvNotify, ok := notify.(platforms.ChannelRecvEventNotify)
310+
if ok {
311+
ref(ctx, recvNotify.Value, idx)
312+
}
313+
return nil
314+
},
315+
}
316+
}
317+
return AwaitBlock{line: line, futs: futs}
318+
}
319+
320+
func Deser[T any](line int, data func(Context) []byte, ret func(Context, T)) SyncBlock {
321+
return Sync(line, func(ctx Context) {
322+
var v T
323+
msgpack.Unmarshal(data(ctx), &v)
324+
ret(ctx, v)
325+
})
326+
}
327+
297328
func Program(body Block, source string, name string) ProgramBlock {
298329
return ProgramBlock{body: body, Source: source, Name: name}
299330
}

pkg/runtimes/dupher/compiler.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,16 @@ func compileBlock(ctx *compileContext, blk Block) {
6767
}
6868

6969
case AwaitBlock:
70+
starts := make([]StartFunc, len(b.futs))
71+
triggers := make([]TriggerFunc, len(b.futs))
72+
for i, f := range b.futs {
73+
starts[i] = f.start
74+
triggers[i] = f.trigger
75+
}
76+
7077
ctx.instructions = append(ctx.instructions, Select{
71-
Start: []StartFunc{b.fut.start},
72-
Trigger: []TriggerFunc{b.fut.trigger},
78+
Start: starts,
79+
Trigger: triggers,
7380
Line: b.line,
7481
})
7582

pkg/runtimes/dupher/worker_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,66 @@ func MutexParentFlowSpec() {
544544
{sleep: 3 * time.Second, wantVars: map[string]any{"r1": 1, "r2": 1}},
545545
},
546546
},
547+
{
548+
name: "select_channel",
549+
flowName: "SelectParentFlow",
550+
source: map[string]string{
551+
"selectchildfast.ds.go": `package main
552+
553+
import (
554+
"github.com/RainingComputers/deepslate/prelude"
555+
"sync"
556+
"time"
557+
)
558+
559+
func SelectChildFastSpec(ch chan int, wg sync.WaitGroup) {
560+
prelude.Sleep(1 * time.Second)
561+
ch <- 1
562+
wg.Done()
563+
}
564+
`,
565+
"selectchildslow.ds.go": `package main
566+
567+
import (
568+
"github.com/RainingComputers/deepslate/prelude"
569+
"sync"
570+
"time"
571+
)
572+
573+
func SelectChildSlowSpec(ch chan int, wg sync.WaitGroup) {
574+
prelude.Sleep(5 * time.Second)
575+
ch <- 2
576+
wg.Done()
577+
}
578+
`,
579+
"selectparent.ds.go": `package main
580+
581+
import "sync"
582+
583+
func SelectParentFlowSpec() {
584+
ch1 := make(chan int)
585+
ch2 := make(chan int)
586+
var wg sync.WaitGroup
587+
wg.Add(2)
588+
go SelectChildFastSpec(ch1, wg)
589+
go SelectChildSlowSpec(ch2, wg)
590+
var result int = 0
591+
select {
592+
case x := <-ch1:
593+
result = x
594+
case y := <-ch2:
595+
result = y
596+
}
597+
_ = result
598+
wg.Wait()
599+
}
600+
`,
601+
},
602+
steps: []workerTestStep{
603+
{sleep: 3 * time.Second, wantVars: map[string]any{"result": 1}},
604+
{sleep: 5 * time.Second},
605+
},
606+
},
547607
}
548608

549609
httpAddr, controlAddr, shutdown, err := spawnBinary(tests)

pkg/runtimes/dython/deepslate/blocks.py

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def is_block(self) -> None:
6767

6868
@dataclass
6969
class AwaitBlock:
70-
fut: Future
70+
futs: list[Future]
7171
line: int
7272

7373
def is_block(self) -> None:
@@ -139,7 +139,7 @@ def body_labeled(line: int, label: str, *stmts: Block) -> BodyLabeledBlock:
139139

140140

141141
def await_block(line: int, start: StartFunc, trigger: TriggerFunc) -> AwaitBlock:
142-
return AwaitBlock(fut=Future(start=start, trigger=trigger), line=line)
142+
return AwaitBlock(futs=[Future(start=start, trigger=trigger)], line=line)
143143

144144

145145
def sync(line: int, stmt: Callable[[Context], None]) -> SyncBlock:
@@ -228,6 +228,39 @@ def _trigger(ctx: Context, notify: EventNotify) -> None:
228228
return await_block(line, _start, _trigger)
229229

230230

231+
def recv_any(
232+
line: int,
233+
count: int,
234+
ids: Callable[[Context], list[str]],
235+
ref: Callable[[Context, bytes, int], None],
236+
) -> AwaitBlock:
237+
def _make_future(idx: int) -> Future:
238+
def _start(ctx: Context) -> Event:
239+
return ChannelRecvEvent(id=ids(ctx)[idx])
240+
241+
def _trigger(ctx: Context, notify: EventNotify) -> None:
242+
if hasattr(notify, "value"):
243+
ref(ctx, notify.value, idx)
244+
245+
return Future(start=_start, trigger=_trigger)
246+
247+
return AwaitBlock(futs=[_make_future(i) for i in range(count)], line=line)
248+
249+
250+
def deser(
251+
line: int,
252+
data: Callable[[Context], bytes],
253+
type_: type,
254+
ret: Callable[[Context, Any], None],
255+
) -> SyncBlock:
256+
def _stmt(ctx: Context) -> None:
257+
origin = get_origin(type_)
258+
value = msgspec.msgpack.decode(data(ctx), type=origin if origin else type_)
259+
ret(ctx, value)
260+
261+
return sync(line, _stmt)
262+
263+
231264
def recv(
232265
line: int,
233266
id: Callable[[Context], str],

pkg/runtimes/dython/deepslate/compiler.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,13 @@ def _compile_block(ctx: _CompileContext, blk: Block) -> None:
103103
_compile_block(ctx, stmt)
104104

105105
elif isinstance(blk, AwaitBlock):
106+
starts = [f.start for f in blk.futs]
107+
triggers = [f.trigger for f in blk.futs]
108+
106109
ctx.instructions.append(
107110
Select(
108-
start=[blk.fut.start],
109-
trigger=[blk.fut.trigger],
111+
start=starts,
112+
trigger=triggers,
110113
line=blk.line,
111114
)
112115
)
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
"""Shared constants for deepslate stubs."""
2-
31
_STUB_MSG = "this is a stub function that is expected to be removed by the transpiler"

pkg/runtimes/dython/deepslate/dpslio.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
from typing import Any
2-
31
from deepslate.constants import _STUB_MSG
42

53

@@ -19,19 +17,19 @@ async def release(self) -> None:
1917
raise NotImplementedError(_STUB_MSG)
2018

2119

22-
class TaskGroup:
23-
def create_task(self, coro: object) -> None:
20+
class WaitGroup:
21+
def add(self, n: int) -> None:
2422
raise NotImplementedError(_STUB_MSG)
2523

2624
async def done(self) -> None:
2725
raise NotImplementedError(_STUB_MSG)
2826

29-
async def __aenter__(self) -> "TaskGroup":
30-
raise NotImplementedError(_STUB_MSG)
31-
32-
async def __aexit__(self, *args: Any) -> None:
27+
async def wait(self) -> None:
3328
raise NotImplementedError(_STUB_MSG)
3429

3530

3631
def create_task(coro: object) -> None:
3732
raise NotImplementedError(_STUB_MSG)
33+
34+
35+
select = object()

pkg/runtimes/dython/deepslate/imports.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ def _dython_type(
355355
if annotation is dpslio.Lock:
356356
return "ds.MutexRef", ""
357357

358-
if annotation is dpslio.TaskGroup:
358+
if annotation is dpslio.WaitGroup:
359359
return "ds.WgRef", ""
360360

361361
if inspect.isclass(annotation) and dataclasses.is_dataclass(annotation):

0 commit comments

Comments
 (0)