szaydel
3/16/2018 - 6:58 PM

Semaphore Example Implementation in Golang

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
	}
}