+64
@@ -0,0 +1,64 @@
|
||||
package limiter
|
||||
|
||||
import "sync"
|
||||
|
||||
type Key = interface{}
|
||||
|
||||
// Manages resources with a limited number of concurrent slots for use for each key.
|
||||
type Instance struct {
|
||||
SlotsPerKey int
|
||||
|
||||
mu sync.Mutex
|
||||
// Limits concurrent use of a resource. Push into the channel to use a slot, and receive to free
|
||||
// up a slot.
|
||||
active map[Key]*activeValueType
|
||||
}
|
||||
|
||||
type activeValueType struct {
|
||||
ch chan struct{}
|
||||
refs int
|
||||
}
|
||||
|
||||
type ActiveValueRef struct {
|
||||
v *activeValueType
|
||||
k Key
|
||||
i *Instance
|
||||
}
|
||||
|
||||
// Returns the limiting channel. Send to it to obtain a slot, and receive to release the slot.
|
||||
func (me ActiveValueRef) C() chan struct{} {
|
||||
return me.v.ch
|
||||
}
|
||||
|
||||
// Drop the reference to a key, this allows keys to be reclaimed when they're no longer in use.
|
||||
func (me ActiveValueRef) Drop() {
|
||||
me.i.mu.Lock()
|
||||
defer me.i.mu.Unlock()
|
||||
me.v.refs--
|
||||
if me.v.refs == 0 {
|
||||
delete(me.i.active, me.k)
|
||||
}
|
||||
}
|
||||
|
||||
// Get a reference to the values for a key. You should make sure to call Drop exactly once on the
|
||||
// returned value when done.
|
||||
func (i *Instance) GetRef(key Key) ActiveValueRef {
|
||||
i.mu.Lock()
|
||||
defer i.mu.Unlock()
|
||||
if i.active == nil {
|
||||
i.active = make(map[Key]*activeValueType)
|
||||
}
|
||||
v, ok := i.active[key]
|
||||
if !ok {
|
||||
v = &activeValueType{
|
||||
ch: make(chan struct{}, i.SlotsPerKey),
|
||||
}
|
||||
i.active[key] = v
|
||||
}
|
||||
v.refs++
|
||||
return ActiveValueRef{
|
||||
v: v,
|
||||
k: key,
|
||||
i: i,
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
// Package testutil contains stuff for testing torrent-related behaviour.
|
||||
//
|
||||
// "greeting" is a single-file torrent of a file called "greeting" that
|
||||
// "contains "hello, world\n".
|
||||
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/anacrolix/torrent/metainfo"
|
||||
)
|
||||
|
||||
var Greeting = Torrent{
|
||||
Files: []File{{
|
||||
Data: GreetingFileContents,
|
||||
}},
|
||||
Name: GreetingFileName,
|
||||
}
|
||||
|
||||
const (
|
||||
// A null in the middle triggers an error if SQLite stores data as text instead of blob.
|
||||
GreetingFileContents = "hello,\x00world\n"
|
||||
GreetingFileName = "greeting"
|
||||
)
|
||||
|
||||
func CreateDummyTorrentData(dirName string) string {
|
||||
f, _ := os.Create(filepath.Join(dirName, "greeting"))
|
||||
defer f.Close()
|
||||
f.WriteString(GreetingFileContents)
|
||||
return f.Name()
|
||||
}
|
||||
|
||||
func GreetingMetaInfo() *metainfo.MetaInfo {
|
||||
return Greeting.Metainfo(5)
|
||||
}
|
||||
|
||||
// Gives a temporary directory containing the completed "greeting" torrent,
|
||||
// and a corresponding metainfo describing it. The temporary directory can be
|
||||
// cleaned away with os.RemoveAll.
|
||||
func GreetingTestTorrent() (tempDir string, metaInfo *metainfo.MetaInfo) {
|
||||
tempDir, err := ioutil.TempDir(os.TempDir(), "")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
CreateDummyTorrentData(tempDir)
|
||||
metaInfo = GreetingMetaInfo()
|
||||
return
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/anacrolix/missinggo/expect"
|
||||
|
||||
"github.com/anacrolix/torrent/bencode"
|
||||
"github.com/anacrolix/torrent/metainfo"
|
||||
)
|
||||
|
||||
type File struct {
|
||||
Name string
|
||||
Data string
|
||||
}
|
||||
|
||||
type Torrent struct {
|
||||
Files []File
|
||||
Name string
|
||||
}
|
||||
|
||||
func (t *Torrent) IsDir() bool {
|
||||
return len(t.Files) == 1 && t.Files[0].Name == ""
|
||||
}
|
||||
|
||||
func (t *Torrent) GetFile(name string) *File {
|
||||
if t.IsDir() && t.Name == name {
|
||||
return &t.Files[0]
|
||||
}
|
||||
for _, f := range t.Files {
|
||||
if f.Name == name {
|
||||
return &f
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Torrent) Info(pieceLength int64) metainfo.Info {
|
||||
info := metainfo.Info{
|
||||
Name: t.Name,
|
||||
PieceLength: pieceLength,
|
||||
}
|
||||
if t.IsDir() {
|
||||
info.Length = int64(len(t.Files[0].Data))
|
||||
}
|
||||
err := info.GeneratePieces(func(fi metainfo.FileInfo) (io.ReadCloser, error) {
|
||||
return ioutil.NopCloser(strings.NewReader(t.GetFile(strings.Join(fi.Path, "/")).Data)), nil
|
||||
})
|
||||
expect.Nil(err)
|
||||
return info
|
||||
}
|
||||
|
||||
func (t *Torrent) Metainfo(pieceLength int64) *metainfo.MetaInfo {
|
||||
mi := metainfo.MetaInfo{}
|
||||
var err error
|
||||
mi.InfoBytes, err = bencode.Marshal(t.Info(pieceLength))
|
||||
expect.Nil(err)
|
||||
return &mi
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
_ "github.com/anacrolix/envpprof"
|
||||
)
|
||||
|
||||
type StatusWriter interface {
|
||||
WriteStatus(io.Writer)
|
||||
}
|
||||
|
||||
// The key is the route pattern. The value is nil when the resource is released.
|
||||
var (
|
||||
mu sync.Mutex
|
||||
sws = map[string]StatusWriter{}
|
||||
)
|
||||
|
||||
func ExportStatusWriter(sw StatusWriter, path string, t *testing.T) (release func()) {
|
||||
pattern := fmt.Sprintf("/%s/%s", t.Name(), path)
|
||||
t.Logf("exporting status path %q", pattern)
|
||||
release = func() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
sws[pattern] = nil
|
||||
}
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if curSw, ok := sws[pattern]; ok {
|
||||
if curSw != nil {
|
||||
panic(fmt.Sprintf("%q still in use", pattern))
|
||||
}
|
||||
sws[pattern] = sw
|
||||
return
|
||||
}
|
||||
http.HandleFunc(
|
||||
pattern,
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
sw := sws[pattern]
|
||||
if sw == nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
sw.WriteStatus(w)
|
||||
},
|
||||
)
|
||||
sws[pattern] = sw
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user