Skip to content

Instantly share code, notes, and snippets.

@jeanbza
Created August 25, 2021 14:35
Show Gist options
  • Save jeanbza/1edaa31c0df91dce9609b6737aee8750 to your computer and use it in GitHub Desktop.
Save jeanbza/1edaa31c0df91dce9609b6737aee8750 to your computer and use it in GitHub Desktop.
sema.go
package jdksync
import (
"sync"
)
type Sema struct {
s chan chan struct{}
mu sync.Mutex
done chan struct{}
waiting []chan struct{}
}
func NewSema() *Sema {
return &Sema{
// Needs buffer=1 so that the Block can put something on and then return,
// guaranteeing that Block, Unblock don't race. (when Block returns, you
// are guaranteed to actually be blocking)
s: make(chan chan struct{}, 1),
mu: sync.Mutex{},
done: make(chan struct{}),
}
}
func (s *Sema) Block() {
once := make(chan struct{})
onceOnce := sync.Once{}
done := make(chan struct{})
s.mu.Lock()
s.done = done
s.mu.Unlock()
go func() {
for {
next := make(chan struct{})
s.mu.Lock()
s.waiting = append(s.waiting, next)
s.mu.Unlock()
select {
case s.s <- next:
onceOnce.Do(func() {
close(once)
})
case <-done:
return
}
}
}()
select {
case <-once:
}
}
func (s *Sema) Unblock() {
s.mu.Lock()
close(s.done)
for _, w := range s.waiting {
close(w)
}
s.waiting = []chan struct{}{}
s.mu.Unlock()
}
func (s *Sema) Wait() <-chan struct{} {
select {
case w := <-s.s:
return w
default:
}
w := make(chan struct{})
close(w)
return w
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment