diff --git a/irc/channel.go b/irc/channel.go index 2d0ba9c3..4a5fa646 100644 --- a/irc/channel.go +++ b/irc/channel.go @@ -39,10 +39,10 @@ type Channel struct { userLimit int accountToUMode map[string]modes.Mode history history.Buffer - stateMutex sync.RWMutex // tier 1 - writerSemaphore Semaphore // tier 1.5 - joinPartMutex sync.Mutex // tier 3 - ensureLoaded utils.Once // manages loading stored registration info from the database + stateMutex sync.RWMutex // tier 1 + writerSemaphore utils.Semaphore // tier 1.5 + joinPartMutex sync.Mutex // tier 3 + ensureLoaded utils.Once // manages loading stored registration info from the database dirtyBits uint } diff --git a/irc/semaphores.go b/irc/semaphores.go index c1968b67..d6008c58 100644 --- a/irc/semaphores.go +++ b/irc/semaphores.go @@ -3,9 +3,9 @@ package irc import ( - "log" "runtime" - "runtime/debug" + + "github.com/oragono/oragono/irc/utils" ) // See #237 for context. Operations that might allocate large amounts of temporary @@ -21,15 +21,12 @@ const ( MaxServerSemaphoreCapacity = 32 ) -// Semaphore is a counting semaphore. Note that a capacity of n requires O(n) storage. -type Semaphore (chan bool) - // ServerSemaphores includes a named Semaphore corresponding to each concurrency-limited // sever operation. type ServerSemaphores struct { // each distinct operation MUST have its own semaphore; // methods that acquire a semaphore MUST NOT call methods that acquire another - ClientDestroy Semaphore + ClientDestroy utils.Semaphore } // Initialize initializes a set of server semaphores. @@ -41,40 +38,3 @@ func (serversem *ServerSemaphores) Initialize() { serversem.ClientDestroy.Initialize(capacity) return } - -// Initialize initializes a semaphore to a given capacity. -func (semaphore *Semaphore) Initialize(capacity int) { - *semaphore = make(chan bool, capacity) - for i := 0; i < capacity; i++ { - (*semaphore) <- true - } -} - -// Acquire acquires a semaphore, blocking if necessary. -func (semaphore *Semaphore) Acquire() { - <-(*semaphore) -} - -// TryAcquire tries to acquire a semaphore, returning whether the acquire was -// successful. It never blocks. -func (semaphore *Semaphore) TryAcquire() (acquired bool) { - select { - case <-(*semaphore): - return true - default: - return false - } -} - -// Release releases a semaphore. It never blocks. (This is not a license -// to program spurious releases.) -func (semaphore *Semaphore) Release() { - select { - case (*semaphore) <- true: - // good - default: - // spurious release - log.Printf("spurious semaphore release (full to capacity %d)", cap(*semaphore)) - debug.PrintStack() - } -} diff --git a/irc/socket.go b/irc/socket.go index 97c7b66e..f73d3b3d 100644 --- a/irc/socket.go +++ b/irc/socket.go @@ -15,6 +15,8 @@ import ( "strings" "sync" "time" + + "github.com/oragono/oragono/irc/utils" ) var ( @@ -34,7 +36,7 @@ type Socket struct { maxSendQBytes int // this is a trylock enforcing that only one goroutine can write to `conn` at a time - writerSemaphore Semaphore + writerSemaphore utils.Semaphore buffers [][]byte totalLength int diff --git a/irc/utils/semaphores.go b/irc/utils/semaphores.go new file mode 100644 index 00000000..8d3d19ff --- /dev/null +++ b/irc/utils/semaphores.go @@ -0,0 +1,49 @@ +// Copyright (c) 2018 Shivaram Lingamneni + +package utils + +import ( + "log" + "runtime/debug" +) + +// Semaphore is a counting semaphore. Note that a capacity of n requires O(n) storage. +// A semaphore of capacity 1 can be used as a trylock. +type Semaphore (chan bool) + +// Initialize initializes a semaphore to a given capacity. +func (semaphore *Semaphore) Initialize(capacity int) { + *semaphore = make(chan bool, capacity) + for i := 0; i < capacity; i++ { + (*semaphore) <- true + } +} + +// Acquire acquires a semaphore, blocking if necessary. +func (semaphore *Semaphore) Acquire() { + <-(*semaphore) +} + +// TryAcquire tries to acquire a semaphore, returning whether the acquire was +// successful. It never blocks. +func (semaphore *Semaphore) TryAcquire() (acquired bool) { + select { + case <-(*semaphore): + return true + default: + return false + } +} + +// Release releases a semaphore. It never blocks. (This is not a license +// to program spurious releases.) +func (semaphore *Semaphore) Release() { + select { + case (*semaphore) <- true: + // good + default: + // spurious release + log.Printf("spurious semaphore release (full to capacity %d)", cap(*semaphore)) + debug.PrintStack() + } +}