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
9 changes: 8 additions & 1 deletion go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCb
github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s=
github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
Expand All @@ -79,20 +81,25 @@ golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDA
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2 h1:+jnHzr9VPj32ykQVai5DNahi9+NSp7yYuCsl5eAQtL0=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2 h1:IRJeR9r1pYWsHKTRe/IInb7lYvbBVIqOgsX/u0mbOWY=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457 h1:zf5N6UOrA487eEFacMePxjXAJctxKmyjKUsjA11Uzuk=
golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
Expand Down
43 changes: 43 additions & 0 deletions lockset/executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -447,3 +447,46 @@ func TestDedup(t *testing.T) {
// Ensure that the source was not modified.
r.Equal(src, cpy)
}

func TestPhilosophers(t *testing.T) {
ctx := context.Background()

dine := func(ctx context.Context, chopsticks []string) error {
return nil
}

// Construct a new executor that will control access to resources identified by strings
exe := NewExecutor[string](GoRunner(ctx))

// log completion, for testing
log := make([]string, 0)
mu := sync.Mutex{}

monitoring := &Events[string]{
OnComplete: func(task Task[string], sinceScheduled time.Duration) {
mu.Lock()
defer mu.Unlock()
log = append(log, task.Keys()...)
},
}

exe.SetEvents(monitoring)

// Set up our tasks, with the usual dining-philosophers constraints: five actors, five "forks" labeled a-e
alice := TaskFunc([]string{"a", "b"}, dine)
bob := TaskFunc([]string{"b", "c"}, dine)
carol := TaskFunc([]string{"c", "d"}, dine)
dave := TaskFunc([]string{"d", "e"}, dine)
eve := TaskFunc([]string{"e", "a"}, dine)

aliceOut, _ := exe.Schedule(alice)
bobOut, _ := exe.Schedule(bob)
carolOut, _ := exe.Schedule(carol)
daveOut, _ := exe.Schedule(dave)
eveOut, _ := exe.Schedule(eve)

r := require.New(t)

r.NoError(Wait(ctx, []Outcome{aliceOut, bobOut, carolOut, daveOut, eveOut}))
r.Equal([]string{"a", "b", "b", "c", "c", "d", "d", "e", "e", "a"}, log)
}
34 changes: 32 additions & 2 deletions lockset/lockset.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,36 @@
//
// SPDX-License-Identifier: Apache-2.0

// Package lockset contains utilities for ordering access to
// potentially-overlapping resources.
/*
Package lockset contains utilities for ordering access to
potentially-overlapping resources.

A simplified dining philosophers problem might look like this:

// This function is a placeholder for work to be performed while holding zero or more resource locks
dine := func(ctx context.Context, forks []string) error { return nil }

// Construct a new executor that will control access to resources identified by strings
exe := NewExecutor[string](GoRunner(ctx))

// Set up our tasks. We have three tasks, each of which needs two resources labeled a-c
alice := TaskFunc([]string{"a", "b"}, dine)
bob := TaskFunc([]string{"b", "c"}, dine)
carol := TaskFunc([]string{"c", "a"}, dine)

// Schedule our tasks
aliceOut, _ := exe.Schedule(alice)
bobOut, _ := exe.Schedule(bob)
carolOut, _ := exe.Schedule(carol)

// wait until everyone is done
Wait(ctx, []Outcome{aliceOut, bobOut, carolOut})

You construct an Executor, which will coordinate tasks. Each Task needs zero or more "locks": these are not actually
mutexes, or even software objects per se, but are simply identifiers for the resource being protected under the lock.
When you Schedule a Task, you get an Outcome, which can be awaited.

Also included in this package is Queue, which implements the core lock-queueing logic, but is generic across value
types and could be used in other applications with similar dependency-planning needs.
*/
package lockset
Loading