This is a very naive, but basic enough for easy reasoning example of implementing a Semaphore in Golang.
package main
import (
"fmt"
"time"
)
var (
globalInt int
blocked int // times we checked while blocked
)
type T struct {
}
type semaphore chan T
func (s semaphore) Init(n int) {
for i := 0; i < n; i++ {
s <- T{}
}
}
func (s semaphore) decr() {
fmt.Printf("Decr (available): %d\n", s.Len())
<-s
}
func (s semaphore) incr() {
s <- T{}
fmt.Printf("Incr (available): %d\n", s.Len())
}
func (s semaphore) Len() int {
return len(s)
}
// Take increments number of references, thereby
// decrementing number of available resources.
// When there are 0 available resources, this should
// end-up blocking.
func (s semaphore) Take() {
s.decr()
}
// Release is opposite of Take and reduces number of
// taken resources thereby incrementing number of available
// resources.
func (s semaphore) Release() {
s.incr()
}
// Busy
func (s semaphore) Busy() bool {
return s.Len() == 0
}
func main() {
timeout := make(chan int)
go func() {
time.Sleep(5 * time.Second)
timeout <- 1
}()
sema := make(semaphore, 1)
sema.Init(1)
go func() {
sema.Take()
fmt.Println("(1)")
fmt.Printf("1.1 Before Change: %d\n", globalInt)
time.Sleep(2 * time.Second)
globalInt++
fmt.Printf("1.2 After Change: %d\n", globalInt)
sema.Release()
}()
go func() {
sema.Take()
fmt.Println("(2)")
fmt.Printf("2.1 Before Change: %d\n", globalInt)
globalInt = 10
fmt.Printf("2.2 After Change: %d\n", globalInt)
sema.Release()
}()
sel:
select {
case <-timeout:
fmt.Printf("blocked count: %d\n", blocked)
return
default:
blocked++
goto sel
}
}