Created
June 7, 2011 00:44
-
-
Save kylelemons/1011446 to your computer and use it in GitHub Desktop.
A sketch of a resource manager in Go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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