message/catalog: use more friendly RWMutex

This reduces contention and also significantly
reduces the cost if there are only readers.
The cost is still high using a RWMutex, but good
enough for now.

Change-Id: I0585d58179368896a0388b77dd4a06c9b12aed1f
Reviewed-on: https://go-review.googlesource.com/45101
Run-TryBot: Marcel van Lohuizen <mpvl@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Nigel Tao <nigeltao@golang.org>
diff --git a/message/catalog/catalog.go b/message/catalog/catalog.go
index d4768af..957444c 100644
--- a/message/catalog/catalog.go
+++ b/message/catalog/catalog.go
@@ -144,13 +144,10 @@
 package catalog // import "golang.org/x/text/message/catalog"
 
 // TODO:
+// Some way to freeze a catalog.
+// - Locking on each lockup turns out to be about 50% of the total running time
+//   for some of the benchmarks in the message package.
 // Consider these:
-// - allow compiling messages. Implemented by Messages:
-//       type Compiler interface { compile(w io.Writer) Handler }
-//   where Handler is defined as:
-//       type Handler interface { Format(c Context, data string) error }
-//   Alternatively, the Compile method could be required in the Message
-//   interface.
 // - Sequence type to support sequences in user-defined messages.
 // - Garbage collection: Remove dictionaries that can no longer be reached
 //   as other dictionaries have been added that cover all possible keys.
diff --git a/message/catalog/dict.go b/message/catalog/dict.go
index 979179a..1810fab 100644
--- a/message/catalog/dict.go
+++ b/message/catalog/dict.go
@@ -53,15 +53,15 @@
 }
 
 type store struct {
-	mutex sync.Mutex
+	mutex sync.RWMutex
 	index map[language.Tag]msgMap
 }
 
 type msgMap map[string]string
 
 func (s *store) lookup(tag language.Tag, key string) (data string, ok bool) {
-	s.mutex.Lock()
-	defer s.mutex.Unlock()
+	s.mutex.RLock()
+	defer s.mutex.RUnlock()
 
 	for ; ; tag = tag.Parent() {
 		if msgs, ok := s.index[tag]; ok {
@@ -78,8 +78,8 @@
 
 // Languages returns all languages for which the store contains variants.
 func (s *store) languages() []language.Tag {
-	s.mutex.Lock()
-	defer s.mutex.Unlock()
+	s.mutex.RLock()
+	defer s.mutex.RUnlock()
 
 	tags := make([]language.Tag, 0, len(s.index))
 	for t := range s.index {