David Symonds | 3cc702b | 2009-04-19 21:17:27 -0700 | [diff] [blame] | 1 | // Copyright 2009 The Go Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
Rob Pike | 0632bb4 | 2009-09-30 13:11:33 -0700 | [diff] [blame] | 5 | package expvar |
David Symonds | 3cc702b | 2009-04-19 21:17:27 -0700 | [diff] [blame] | 6 | |
| 7 | import ( |
Brad Fitzpatrick | cfb9cf0 | 2014-01-20 09:59:23 -0800 | [diff] [blame] | 8 | "bytes" |
Rob Pike | 30aa701 | 2011-11-08 15:40:58 -0800 | [diff] [blame] | 9 | "encoding/json" |
Matt T. Proud | 926616d | 2015-02-02 00:33:44 -0800 | [diff] [blame] | 10 | "math" |
Evan Phoenix | fc22fb8 | 2015-01-30 15:31:47 -0800 | [diff] [blame] | 11 | "net" |
Brad Fitzpatrick | cfb9cf0 | 2014-01-20 09:59:23 -0800 | [diff] [blame] | 12 | "net/http/httptest" |
Evan Phoenix | fc22fb8 | 2015-01-30 15:31:47 -0800 | [diff] [blame] | 13 | "runtime" |
Brad Fitzpatrick | cfb9cf0 | 2014-01-20 09:59:23 -0800 | [diff] [blame] | 14 | "strconv" |
Evan Phoenix | fc22fb8 | 2015-01-30 15:31:47 -0800 | [diff] [blame] | 15 | "sync" |
Matt T. Proud | 926616d | 2015-02-02 00:33:44 -0800 | [diff] [blame] | 16 | "sync/atomic" |
Robert Griesemer | 1c72959 | 2009-12-15 15:27:16 -0800 | [diff] [blame] | 17 | "testing" |
David Symonds | 3cc702b | 2009-04-19 21:17:27 -0700 | [diff] [blame] | 18 | ) |
| 19 | |
David Symonds | 715588f | 2012-02-04 14:32:05 +1100 | [diff] [blame] | 20 | // RemoveAll removes all exported variables. |
| 21 | // This is for tests only. |
| 22 | func RemoveAll() { |
| 23 | mutex.Lock() |
| 24 | defer mutex.Unlock() |
| 25 | vars = make(map[string]Var) |
Brad Fitzpatrick | cfb9cf0 | 2014-01-20 09:59:23 -0800 | [diff] [blame] | 26 | varKeys = nil |
David Symonds | 715588f | 2012-02-04 14:32:05 +1100 | [diff] [blame] | 27 | } |
| 28 | |
Rick Arnold | 5abd327 | 2016-02-24 19:25:38 -0500 | [diff] [blame] | 29 | func TestNil(t *testing.T) { |
| 30 | RemoveAll() |
| 31 | val := Get("missing") |
| 32 | if val != nil { |
| 33 | t.Errorf("got %v, want nil", val) |
| 34 | } |
| 35 | } |
| 36 | |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 37 | func TestInt(t *testing.T) { |
Rémy Oudompheng | 7510423 | 2013-01-27 00:24:09 +0100 | [diff] [blame] | 38 | RemoveAll() |
Robert Griesemer | 1c72959 | 2009-12-15 15:27:16 -0800 | [diff] [blame] | 39 | reqs := NewInt("requests") |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 40 | if reqs.i != 0 { |
Russ Cox | c634189 | 2010-01-22 14:24:17 -0800 | [diff] [blame] | 41 | t.Errorf("reqs.i = %v, want 0", reqs.i) |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 42 | } |
| 43 | if reqs != Get("requests").(*Int) { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 44 | t.Errorf("Get() failed.") |
David Symonds | 3cc702b | 2009-04-19 21:17:27 -0700 | [diff] [blame] | 45 | } |
| 46 | |
Robert Griesemer | 1c72959 | 2009-12-15 15:27:16 -0800 | [diff] [blame] | 47 | reqs.Add(1) |
| 48 | reqs.Add(3) |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 49 | if reqs.i != 4 { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 50 | t.Errorf("reqs.i = %v, want 4", reqs.i) |
David Symonds | 3cc702b | 2009-04-19 21:17:27 -0700 | [diff] [blame] | 51 | } |
| 52 | |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 53 | if s := reqs.String(); s != "4" { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 54 | t.Errorf("reqs.String() = %q, want \"4\"", s) |
David Symonds | 3cc702b | 2009-04-19 21:17:27 -0700 | [diff] [blame] | 55 | } |
Sam Thorogood | 3b9c9d2 | 2010-10-11 13:14:07 -0400 | [diff] [blame] | 56 | |
| 57 | reqs.Set(-2) |
| 58 | if reqs.i != -2 { |
| 59 | t.Errorf("reqs.i = %v, want -2", reqs.i) |
| 60 | } |
David Symonds | 3cc702b | 2009-04-19 21:17:27 -0700 | [diff] [blame] | 61 | } |
| 62 | |
Evan Phoenix | fc22fb8 | 2015-01-30 15:31:47 -0800 | [diff] [blame] | 63 | func BenchmarkIntAdd(b *testing.B) { |
| 64 | var v Int |
| 65 | |
| 66 | b.RunParallel(func(pb *testing.PB) { |
| 67 | for pb.Next() { |
| 68 | v.Add(1) |
| 69 | } |
| 70 | }) |
| 71 | } |
| 72 | |
| 73 | func BenchmarkIntSet(b *testing.B) { |
| 74 | var v Int |
| 75 | |
| 76 | b.RunParallel(func(pb *testing.PB) { |
| 77 | for pb.Next() { |
| 78 | v.Set(1) |
| 79 | } |
| 80 | }) |
| 81 | } |
| 82 | |
Matt T. Proud | 926616d | 2015-02-02 00:33:44 -0800 | [diff] [blame] | 83 | func (v *Float) val() float64 { |
| 84 | return math.Float64frombits(atomic.LoadUint64(&v.f)) |
| 85 | } |
| 86 | |
Jos Visser | 25f762c | 2011-01-18 11:24:43 -0500 | [diff] [blame] | 87 | func TestFloat(t *testing.T) { |
Rémy Oudompheng | 7510423 | 2013-01-27 00:24:09 +0100 | [diff] [blame] | 88 | RemoveAll() |
Jos Visser | 25f762c | 2011-01-18 11:24:43 -0500 | [diff] [blame] | 89 | reqs := NewFloat("requests-float") |
| 90 | if reqs.f != 0.0 { |
| 91 | t.Errorf("reqs.f = %v, want 0", reqs.f) |
| 92 | } |
| 93 | if reqs != Get("requests-float").(*Float) { |
| 94 | t.Errorf("Get() failed.") |
| 95 | } |
| 96 | |
| 97 | reqs.Add(1.5) |
| 98 | reqs.Add(1.25) |
Matt T. Proud | 926616d | 2015-02-02 00:33:44 -0800 | [diff] [blame] | 99 | if v := reqs.val(); v != 2.75 { |
| 100 | t.Errorf("reqs.val() = %v, want 2.75", v) |
Jos Visser | 25f762c | 2011-01-18 11:24:43 -0500 | [diff] [blame] | 101 | } |
| 102 | |
| 103 | if s := reqs.String(); s != "2.75" { |
| 104 | t.Errorf("reqs.String() = %q, want \"4.64\"", s) |
| 105 | } |
| 106 | |
| 107 | reqs.Add(-2) |
Matt T. Proud | 926616d | 2015-02-02 00:33:44 -0800 | [diff] [blame] | 108 | if v := reqs.val(); v != 0.75 { |
| 109 | t.Errorf("reqs.val() = %v, want 0.75", v) |
Jos Visser | 25f762c | 2011-01-18 11:24:43 -0500 | [diff] [blame] | 110 | } |
| 111 | } |
| 112 | |
Evan Phoenix | fc22fb8 | 2015-01-30 15:31:47 -0800 | [diff] [blame] | 113 | func BenchmarkFloatAdd(b *testing.B) { |
| 114 | var f Float |
| 115 | |
| 116 | b.RunParallel(func(pb *testing.PB) { |
| 117 | for pb.Next() { |
| 118 | f.Add(1.0) |
| 119 | } |
| 120 | }) |
| 121 | } |
| 122 | |
| 123 | func BenchmarkFloatSet(b *testing.B) { |
| 124 | var f Float |
| 125 | |
| 126 | b.RunParallel(func(pb *testing.PB) { |
| 127 | for pb.Next() { |
| 128 | f.Set(1.0) |
| 129 | } |
| 130 | }) |
| 131 | } |
| 132 | |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 133 | func TestString(t *testing.T) { |
Rémy Oudompheng | 7510423 | 2013-01-27 00:24:09 +0100 | [diff] [blame] | 134 | RemoveAll() |
Robert Griesemer | 1c72959 | 2009-12-15 15:27:16 -0800 | [diff] [blame] | 135 | name := NewString("my-name") |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 136 | if name.s != "" { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 137 | t.Errorf("name.s = %q, want \"\"", name.s) |
David Symonds | f4b92c8 | 2009-04-26 20:57:01 -0700 | [diff] [blame] | 138 | } |
| 139 | |
Robert Griesemer | 1c72959 | 2009-12-15 15:27:16 -0800 | [diff] [blame] | 140 | name.Set("Mike") |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 141 | if name.s != "Mike" { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 142 | t.Errorf("name.s = %q, want \"Mike\"", name.s) |
David Symonds | de489fb | 2009-04-20 22:38:14 -0700 | [diff] [blame] | 143 | } |
| 144 | |
David Symonds | 6f2a881 | 2016-04-06 11:53:26 +1000 | [diff] [blame] | 145 | if s, want := name.String(), `"Mike"`; s != want { |
| 146 | t.Errorf("from %q, name.String() = %q, want %q", name.s, s, want) |
| 147 | } |
| 148 | |
| 149 | // Make sure we produce safe JSON output. |
| 150 | name.Set(`<`) |
| 151 | if s, want := name.String(), "\"\\u003c\""; s != want { |
| 152 | t.Errorf("from %q, name.String() = %q, want %q", name.s, s, want) |
David Symonds | de489fb | 2009-04-20 22:38:14 -0700 | [diff] [blame] | 153 | } |
| 154 | } |
| 155 | |
Evan Phoenix | fc22fb8 | 2015-01-30 15:31:47 -0800 | [diff] [blame] | 156 | func BenchmarkStringSet(b *testing.B) { |
| 157 | var s String |
| 158 | |
| 159 | b.RunParallel(func(pb *testing.PB) { |
| 160 | for pb.Next() { |
| 161 | s.Set("red") |
| 162 | } |
| 163 | }) |
| 164 | } |
| 165 | |
David Symonds | de489fb | 2009-04-20 22:38:14 -0700 | [diff] [blame] | 166 | func TestMapCounter(t *testing.T) { |
Rémy Oudompheng | 7510423 | 2013-01-27 00:24:09 +0100 | [diff] [blame] | 167 | RemoveAll() |
Robert Hencke | c8727c8 | 2011-05-18 13:14:56 -0400 | [diff] [blame] | 168 | colors := NewMap("bike-shed-colors") |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 169 | |
Robert Hencke | c8727c8 | 2011-05-18 13:14:56 -0400 | [diff] [blame] | 170 | colors.Add("red", 1) |
| 171 | colors.Add("red", 2) |
| 172 | colors.Add("blue", 4) |
Rui Ueyama | 74c6b84 | 2014-04-10 21:14:04 -0700 | [diff] [blame] | 173 | colors.AddFloat(`green "midori"`, 4.125) |
Robert Hencke | c8727c8 | 2011-05-18 13:14:56 -0400 | [diff] [blame] | 174 | if x := colors.m["red"].(*Int).i; x != 3 { |
| 175 | t.Errorf("colors.m[\"red\"] = %v, want 3", x) |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 176 | } |
Robert Hencke | c8727c8 | 2011-05-18 13:14:56 -0400 | [diff] [blame] | 177 | if x := colors.m["blue"].(*Int).i; x != 4 { |
| 178 | t.Errorf("colors.m[\"blue\"] = %v, want 4", x) |
David Symonds | de489fb | 2009-04-20 22:38:14 -0700 | [diff] [blame] | 179 | } |
Matt T. Proud | 926616d | 2015-02-02 00:33:44 -0800 | [diff] [blame] | 180 | if x := colors.m[`green "midori"`].(*Float).val(); x != 4.125 { |
| 181 | t.Errorf("colors.m[`green \"midori\"] = %v, want 4.125", x) |
Jos Visser | 25f762c | 2011-01-18 11:24:43 -0500 | [diff] [blame] | 182 | } |
David Symonds | de489fb | 2009-04-20 22:38:14 -0700 | [diff] [blame] | 183 | |
Robert Hencke | c8727c8 | 2011-05-18 13:14:56 -0400 | [diff] [blame] | 184 | // colors.String() should be '{"red":3, "blue":4}', |
David Symonds | 2f28494 | 2009-05-04 15:14:22 -0700 | [diff] [blame] | 185 | // though the order of red and blue could vary. |
Robert Hencke | c8727c8 | 2011-05-18 13:14:56 -0400 | [diff] [blame] | 186 | s := colors.String() |
Russ Cox | dba9d62 | 2010-04-21 16:40:53 -0700 | [diff] [blame] | 187 | var j interface{} |
| 188 | err := json.Unmarshal([]byte(s), &j) |
Sergey 'SnakE' Gromov | 9d50b46 | 2009-11-30 13:55:09 -0800 | [diff] [blame] | 189 | if err != nil { |
Robert Hencke | c8727c8 | 2011-05-18 13:14:56 -0400 | [diff] [blame] | 190 | t.Errorf("colors.String() isn't valid JSON: %v", err) |
David Symonds | de489fb | 2009-04-20 22:38:14 -0700 | [diff] [blame] | 191 | } |
Robert Griesemer | 1c72959 | 2009-12-15 15:27:16 -0800 | [diff] [blame] | 192 | m, ok := j.(map[string]interface{}) |
Sergey 'SnakE' Gromov | 9d50b46 | 2009-11-30 13:55:09 -0800 | [diff] [blame] | 193 | if !ok { |
Robert Hencke | c8727c8 | 2011-05-18 13:14:56 -0400 | [diff] [blame] | 194 | t.Error("colors.String() didn't produce a map.") |
David Symonds | de489fb | 2009-04-20 22:38:14 -0700 | [diff] [blame] | 195 | } |
Robert Griesemer | 1c72959 | 2009-12-15 15:27:16 -0800 | [diff] [blame] | 196 | red := m["red"] |
| 197 | x, ok := red.(float64) |
Sergey 'SnakE' Gromov | 9d50b46 | 2009-11-30 13:55:09 -0800 | [diff] [blame] | 198 | if !ok { |
| 199 | t.Error("red.Kind() is not a number.") |
David Symonds | 3cc702b | 2009-04-19 21:17:27 -0700 | [diff] [blame] | 200 | } |
Sergey 'SnakE' Gromov | 9d50b46 | 2009-11-30 13:55:09 -0800 | [diff] [blame] | 201 | if x != 3 { |
Rob Pike | 1959c3a | 2010-09-23 13:48:56 +1000 | [diff] [blame] | 202 | t.Errorf("red = %v, want 3", x) |
David Symonds | 3cc702b | 2009-04-19 21:17:27 -0700 | [diff] [blame] | 203 | } |
| 204 | } |
David Symonds | 5a12b18 | 2009-05-24 15:04:43 -0700 | [diff] [blame] | 205 | |
Evan Phoenix | fc22fb8 | 2015-01-30 15:31:47 -0800 | [diff] [blame] | 206 | func BenchmarkMapSet(b *testing.B) { |
| 207 | m := new(Map).Init() |
| 208 | |
| 209 | v := new(Int) |
| 210 | |
| 211 | b.RunParallel(func(pb *testing.PB) { |
| 212 | for pb.Next() { |
| 213 | m.Set("red", v) |
| 214 | } |
| 215 | }) |
| 216 | } |
| 217 | |
| 218 | func BenchmarkMapAddSame(b *testing.B) { |
| 219 | for i := 0; i < b.N; i++ { |
| 220 | m := new(Map).Init() |
| 221 | m.Add("red", 1) |
| 222 | m.Add("red", 1) |
| 223 | m.Add("red", 1) |
| 224 | m.Add("red", 1) |
| 225 | } |
| 226 | } |
| 227 | |
| 228 | func BenchmarkMapAddDifferent(b *testing.B) { |
| 229 | for i := 0; i < b.N; i++ { |
| 230 | m := new(Map).Init() |
| 231 | m.Add("red", 1) |
| 232 | m.Add("blue", 1) |
| 233 | m.Add("green", 1) |
| 234 | m.Add("yellow", 1) |
| 235 | } |
| 236 | } |
| 237 | |
David Symonds | 57d0c26 | 2011-04-15 01:21:18 -0700 | [diff] [blame] | 238 | func TestFunc(t *testing.T) { |
Rémy Oudompheng | 7510423 | 2013-01-27 00:24:09 +0100 | [diff] [blame] | 239 | RemoveAll() |
David Symonds | 57d0c26 | 2011-04-15 01:21:18 -0700 | [diff] [blame] | 240 | var x interface{} = []string{"a", "b"} |
| 241 | f := Func(func() interface{} { return x }) |
| 242 | if s, exp := f.String(), `["a","b"]`; s != exp { |
| 243 | t.Errorf(`f.String() = %q, want %q`, s, exp) |
David Symonds | 5a12b18 | 2009-05-24 15:04:43 -0700 | [diff] [blame] | 244 | } |
| 245 | |
David Symonds | 57d0c26 | 2011-04-15 01:21:18 -0700 | [diff] [blame] | 246 | x = 17 |
| 247 | if s, exp := f.String(), `17`; s != exp { |
| 248 | t.Errorf(`f.String() = %q, want %q`, s, exp) |
David Symonds | 8bfa917 | 2010-12-20 17:35:21 +1100 | [diff] [blame] | 249 | } |
| 250 | } |
Brad Fitzpatrick | cfb9cf0 | 2014-01-20 09:59:23 -0800 | [diff] [blame] | 251 | |
| 252 | func TestHandler(t *testing.T) { |
| 253 | RemoveAll() |
| 254 | m := NewMap("map1") |
| 255 | m.Add("a", 1) |
| 256 | m.Add("z", 2) |
| 257 | m2 := NewMap("map2") |
| 258 | for i := 0; i < 9; i++ { |
| 259 | m2.Add(strconv.Itoa(i), int64(i)) |
| 260 | } |
| 261 | rr := httptest.NewRecorder() |
| 262 | rr.Body = new(bytes.Buffer) |
| 263 | expvarHandler(rr, nil) |
| 264 | want := `{ |
| 265 | "map1": {"a": 1, "z": 2}, |
| 266 | "map2": {"0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8} |
| 267 | } |
| 268 | ` |
| 269 | if got := rr.Body.String(); got != want { |
| 270 | t.Errorf("HTTP handler wrote:\n%s\nWant:\n%s", got, want) |
| 271 | } |
| 272 | } |
Evan Phoenix | fc22fb8 | 2015-01-30 15:31:47 -0800 | [diff] [blame] | 273 | |
| 274 | func BenchmarkRealworldExpvarUsage(b *testing.B) { |
| 275 | var ( |
| 276 | bytesSent Int |
| 277 | bytesRead Int |
| 278 | ) |
| 279 | |
| 280 | // The benchmark creates GOMAXPROCS client/server pairs. |
| 281 | // Each pair creates 4 goroutines: client reader/writer and server reader/writer. |
| 282 | // The benchmark stresses concurrent reading and writing to the same connection. |
| 283 | // Such pattern is used in net/http and net/rpc. |
| 284 | |
| 285 | b.StopTimer() |
| 286 | |
| 287 | P := runtime.GOMAXPROCS(0) |
| 288 | N := b.N / P |
| 289 | W := 1000 |
| 290 | |
| 291 | // Setup P client/server connections. |
| 292 | clients := make([]net.Conn, P) |
| 293 | servers := make([]net.Conn, P) |
Dmitry Vyukov | 08ca401 | 2015-02-05 16:50:11 +0300 | [diff] [blame] | 294 | ln, err := net.Listen("tcp", "127.0.0.1:0") |
Evan Phoenix | fc22fb8 | 2015-01-30 15:31:47 -0800 | [diff] [blame] | 295 | if err != nil { |
| 296 | b.Fatalf("Listen failed: %v", err) |
| 297 | } |
| 298 | defer ln.Close() |
| 299 | done := make(chan bool) |
| 300 | go func() { |
| 301 | for p := 0; p < P; p++ { |
| 302 | s, err := ln.Accept() |
| 303 | if err != nil { |
| 304 | b.Errorf("Accept failed: %v", err) |
| 305 | return |
| 306 | } |
| 307 | servers[p] = s |
| 308 | } |
| 309 | done <- true |
| 310 | }() |
| 311 | for p := 0; p < P; p++ { |
| 312 | c, err := net.Dial("tcp", ln.Addr().String()) |
| 313 | if err != nil { |
| 314 | b.Fatalf("Dial failed: %v", err) |
| 315 | } |
| 316 | clients[p] = c |
| 317 | } |
| 318 | <-done |
| 319 | |
| 320 | b.StartTimer() |
| 321 | |
| 322 | var wg sync.WaitGroup |
| 323 | wg.Add(4 * P) |
| 324 | for p := 0; p < P; p++ { |
| 325 | // Client writer. |
| 326 | go func(c net.Conn) { |
| 327 | defer wg.Done() |
| 328 | var buf [1]byte |
| 329 | for i := 0; i < N; i++ { |
| 330 | v := byte(i) |
| 331 | for w := 0; w < W; w++ { |
| 332 | v *= v |
| 333 | } |
| 334 | buf[0] = v |
| 335 | n, err := c.Write(buf[:]) |
| 336 | if err != nil { |
| 337 | b.Errorf("Write failed: %v", err) |
| 338 | return |
| 339 | } |
| 340 | |
| 341 | bytesSent.Add(int64(n)) |
| 342 | } |
| 343 | }(clients[p]) |
| 344 | |
| 345 | // Pipe between server reader and server writer. |
| 346 | pipe := make(chan byte, 128) |
| 347 | |
| 348 | // Server reader. |
| 349 | go func(s net.Conn) { |
| 350 | defer wg.Done() |
| 351 | var buf [1]byte |
| 352 | for i := 0; i < N; i++ { |
| 353 | n, err := s.Read(buf[:]) |
| 354 | |
| 355 | if err != nil { |
| 356 | b.Errorf("Read failed: %v", err) |
| 357 | return |
| 358 | } |
| 359 | |
| 360 | bytesRead.Add(int64(n)) |
| 361 | pipe <- buf[0] |
| 362 | } |
| 363 | }(servers[p]) |
| 364 | |
| 365 | // Server writer. |
| 366 | go func(s net.Conn) { |
| 367 | defer wg.Done() |
| 368 | var buf [1]byte |
| 369 | for i := 0; i < N; i++ { |
| 370 | v := <-pipe |
| 371 | for w := 0; w < W; w++ { |
| 372 | v *= v |
| 373 | } |
| 374 | buf[0] = v |
| 375 | n, err := s.Write(buf[:]) |
| 376 | if err != nil { |
| 377 | b.Errorf("Write failed: %v", err) |
| 378 | return |
| 379 | } |
| 380 | |
| 381 | bytesSent.Add(int64(n)) |
| 382 | } |
| 383 | s.Close() |
| 384 | }(servers[p]) |
| 385 | |
| 386 | // Client reader. |
| 387 | go func(c net.Conn) { |
| 388 | defer wg.Done() |
| 389 | var buf [1]byte |
| 390 | for i := 0; i < N; i++ { |
| 391 | n, err := c.Read(buf[:]) |
| 392 | |
| 393 | if err != nil { |
| 394 | b.Errorf("Read failed: %v", err) |
| 395 | return |
| 396 | } |
| 397 | |
| 398 | bytesRead.Add(int64(n)) |
| 399 | } |
| 400 | c.Close() |
| 401 | }(clients[p]) |
| 402 | } |
| 403 | wg.Wait() |
| 404 | } |