summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--utils.go3
-rw-r--r--ws.go19
2 files changed, 21 insertions, 1 deletions
diff --git a/utils.go b/utils.go
index 9fa6472..81d2e17 100644
--- a/utils.go
+++ b/utils.go
@@ -23,11 +23,14 @@ package main
import (
"crypto/rand"
"encoding/base64"
+ "errors"
"fmt"
"log"
"net/http"
)
+var errUnexpectedRace = errors.New("unexpected race condition")
+
/*
* Write a string to a http.ResponseWriter, setting the Content-Type and status
* code.
diff --git a/ws.go b/ws.go
index 2a2c457..8bd6c58 100644
--- a/ws.go
+++ b/ws.go
@@ -217,13 +217,30 @@ var (
)
func setupChanPool() error {
- chanPoolLock.Lock()
+ /*
+ * It would be unusual for this function to run concurrently with
+ * anything else that modifies chanPool, so we fail when the lock is
+ * unsuccessful.
+ */
+ r := chanPoolLock.TryLock()
+ if !r {
+ return fmt.Errorf("cannot set up chanPool: %w", errUnexpectedRace)
+ }
defer chanPoolLock.Unlock()
chanPool = make(map[string](*chan string))
return nil
}
func propagate(msg string) {
+ /*
+ * It is not a mistake that we acquire a read lock instead of a write
+ * lock here. Channels provide synchronization, and other than using
+ * the channels, we are simply iterating through chanPoolLock. This is
+ * unsafe when chanPoolLock's structure is being modified, such as
+ * when a channel is being added or deleted from the pool; but it's
+ * fine if other goroutines are simply indexing it and using the
+ * channels.
+ */
chanPoolLock.RLock()
defer chanPoolLock.RUnlock()
for k, v := range chanPool {