Skip to content

Instantly share code, notes, and snippets.

@kylelemons
Created June 7, 2011 00:44
Show Gist options
  • Save kylelemons/1011446 to your computer and use it in GitHub Desktop.
Save kylelemons/1011446 to your computer and use it in GitHub Desktop.
A sketch of a resource manager in Go
package main
import (
"fmt"
"time"
"sync"
)
// A Token represents resources allocated. It must be given
// back to the Manager to free the resource.
type Token int64
// A Request represents an Allocation of a resource.
type Request struct {
Amount int64
Granted chan Token
}
// A Manager manages requests to limited resources.
//
// Example:
// reman := resources.NewManager(1024)
// // ...
// granted := make(chan Token)
// reman.Allocate <- &Request{100, granted}
// token := <-granted
// // ...
// reman.Free <- token
type Manager struct {
avail int64
token Token
Allocate chan *Request
Free chan Token
}
// Create (but don't start) a new Manager with the given resource
// limit.
func NewManager(resources int64) *Manager {
return &Manager{
avail: resources,
Allocate: make(chan *Request),
Free: make(chan Token),
}
}
// Run starts managing resources. It should be run in its own goroutine. It
// returns when the Manager's Allocate channel is closed. All requests have
// equal priority and are first-come first-served; it will not starve a
// high-resource request because low-resource requests continue to come in
// after it.
func (reman *Manager) Run() {
tokens := make(map[Token]int64)
queue := []*Request{}
for {
for len(queue) > 0 {
next := queue[0]
if next.Amount > reman.avail {
break
}
// Remove this request from the queue
queue = queue[1:]
reman.token++
// Store the amount associated with this token
tokens[reman.token] = next.Amount
// Decrement the available resources
reman.avail -= next.Amount
// Grant the request
next.Granted <- reman.token
}
select {
case req := <-reman.Allocate:
// Add this request to the end of the queue
queue = append(queue, req)
case token := <-reman.Free:
// Don't free up resources associated with an invalid token
if amount, ok := tokens[token]; ok {
// Increment the available resources
reman.avail += amount
// Delete the token
tokens[token] = 0, false
}
}
}
}
func main() {
reman := NewManager(100)
all := new(sync.WaitGroup)
id := 0
demo := func(name string, amount int64) {
all.Add(1)
go func(id int) {
defer all.Done()
granted := make(chan Token)
fmt.Printf("%3d: %8s: Request %d\n", id, name, amount)
reman.Allocate <- &Request{amount, granted}
token := <-granted
fmt.Printf("%3d: %8s: Granted %d\n", id, name, amount)
<-time.After(amount*1e6)
fmt.Printf("%3d: %8s: Freeing %d\n", id, name, amount)
reman.Free <- token
}(id)
id++
}
go reman.Run()
demo("Little", 5)
demo("Tiny", 1)
demo("Little", 5)
demo("Little", 5)
demo("Medium", 20)
demo("Medium", 20)
demo("Tiny", 1)
demo("Medium", 20)
demo("Medium", 20)
demo("Medium", 20)
demo("Big", 80)
demo("Big", 80)
demo("Big", 80)
demo("Tiny", 1)
demo("Tiny", 1)
demo("Tiny", 1)
demo("Tiny", 1)
demo("Small", 10)
demo("Small", 10)
demo("Small", 10)
demo("Little", 5)
all.Wait()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment