@@ -15,10 +15,22 @@ import (
15
15
)
16
16
17
17
const shards = 512
18
- const requestsMap = 16
18
+ const requestsMap = 32
19
19
20
20
var epoch = time .Now ()
21
21
22
+ type connShard struct {
23
+ rmut sync.Mutex
24
+ requests [requestsMap ]* Future
25
+ first * Future
26
+ last * * Future
27
+ bufmut sync.Mutex
28
+ buf smallWBuf
29
+ enc * msgpack.Encoder
30
+ bcache smallWBuf
31
+ _pad [16 ]uint64
32
+ }
33
+
22
34
type Connection struct {
23
35
addr string
24
36
c * net.TCPConn
@@ -28,18 +40,11 @@ type Connection struct {
28
40
Schema * Schema
29
41
requestId uint32
30
42
Greeting * Greeting
31
- shard [shards ]struct {
32
- sync.Mutex
33
- count uint32
34
- requests [requestsMap ]* Future
35
- first * Future
36
- last * * Future
37
- buf smallWBuf
38
- enc * msgpack.Encoder
39
- bcache smallWBuf
40
- }
43
+
44
+ shard [shards ]connShard
45
+ dirtyShard chan uint32
46
+
41
47
rlimit chan struct {}
42
- packets chan struct {}
43
48
control chan struct {}
44
49
opts Opts
45
50
closed bool
@@ -62,14 +67,14 @@ type Opts struct {
62
67
func Connect (addr string , opts Opts ) (conn * Connection , err error ) {
63
68
64
69
conn = & Connection {
65
- addr : addr ,
66
- requestId : 0 ,
67
- Greeting : & Greeting {},
68
- rlimit : make (chan struct {}, 1024 * 1024 ),
69
- packets : make (chan struct {}, 1024 * 1024 ),
70
- control : make (chan struct {}),
71
- opts : opts ,
72
- dec : msgpack .NewDecoder (& smallBuf {}),
70
+ addr : addr ,
71
+ requestId : 0 ,
72
+ Greeting : & Greeting {},
73
+ rlimit : make (chan struct {}, 1024 * 1024 ),
74
+ dirtyShard : make (chan uint32 , shards ),
75
+ control : make (chan struct {}),
76
+ opts : opts ,
77
+ dec : msgpack .NewDecoder (& smallBuf {}),
73
78
}
74
79
for i := range conn .shard {
75
80
conn .shard [i ].last = & conn .shard [i ].first
@@ -277,13 +282,13 @@ func (conn *Connection) closeConnection(neterr error, r *bufio.Reader, w *bufio.
277
282
278
283
func (conn * Connection ) lockShards () {
279
284
for i := range conn .shard {
280
- conn .shard [i ].Lock ()
285
+ conn .shard [i ].rmut . Lock ()
281
286
}
282
287
}
283
288
284
289
func (conn * Connection ) unlockShards () {
285
290
for i := range conn .shard {
286
- conn .shard [i ].Unlock ()
291
+ conn .shard [i ].rmut . Unlock ()
287
292
}
288
293
}
289
294
@@ -299,20 +304,20 @@ func (conn *Connection) closeConnectionForever(err error) error {
299
304
func (conn * Connection ) writer () {
300
305
var w * bufio.Writer
301
306
var err error
302
- var shardn int
307
+ var shardn uint32
303
308
Main:
304
309
for ! conn .closed {
305
310
select {
306
- case <- conn .packets :
311
+ case shardn = <- conn .dirtyShard :
307
312
default :
308
313
runtime .Gosched ()
309
- if len (conn .packets ) == 0 && w != nil {
314
+ if len (conn .dirtyShard ) == 0 && w != nil {
310
315
if err := w .Flush (); err != nil {
311
316
_ , w , _ = conn .closeConnection (err , nil , w )
312
317
}
313
318
}
314
319
select {
315
- case <- conn .packets :
320
+ case shardn = <- conn .dirtyShard :
316
321
case <- conn .control :
317
322
return
318
323
}
@@ -323,31 +328,18 @@ Main:
323
328
return
324
329
}
325
330
}
326
- for stop := shardn + shards ; shardn != stop ; shardn ++ {
327
- shard := & conn .shard [shardn & (shards - 1 )]
328
- if nreq := atomic .LoadUint32 (& shard .count ); nreq > 0 {
329
- shard .Lock ()
330
- nreq , shard .count = shard .count , 0
331
- packet := shard .buf
332
- shard .buf = nil
333
- shard .Unlock ()
334
- if err := write (w , packet ); err != nil {
335
- _ , w , _ = conn .closeConnection (err , nil , w )
336
- continue Main
337
- }
338
- shard .Lock ()
339
- shard .bcache = packet [0 :0 ]
340
- shard .Unlock ()
341
- for ; nreq > 1 ; nreq -- {
342
- select {
343
- case <- conn .packets :
344
- default :
345
- break
346
- }
347
- }
348
- break
349
- }
331
+ shard := & conn .shard [shardn ]
332
+ shard .bufmut .Lock ()
333
+ packet := shard .buf
334
+ shard .buf = nil
335
+ shard .bufmut .Unlock ()
336
+ if err := write (w , packet ); err != nil {
337
+ _ , w , _ = conn .closeConnection (err , nil , w )
338
+ continue Main
350
339
}
340
+ shard .bufmut .Lock ()
341
+ shard .bcache = packet [0 :0 ]
342
+ shard .bufmut .Unlock ()
351
343
}
352
344
}
353
345
@@ -385,24 +377,31 @@ func (conn *Connection) reader() {
385
377
func (conn * Connection ) putFuture (fut * Future , body func (* msgpack.Encoder ) error ) {
386
378
shardn := fut .requestId & (shards - 1 )
387
379
shard := & conn .shard [shardn ]
388
- shard .Lock ()
389
- if conn .closed {
390
- shard .Unlock ()
391
- fut .err = ClientError {ErrConnectionClosed , "using closed connection" }
392
- return
393
- }
380
+ shard .bufmut .Lock ()
381
+ firstWritten := len (shard .buf ) == 0
394
382
if cap (shard .buf ) == 0 {
395
383
shard .buf , shard .bcache = shard .bcache , nil
396
384
if cap (shard .buf ) == 0 {
397
385
shard .buf = make (smallWBuf , 0 , 128 )
398
386
}
399
387
shard .enc = msgpack .NewEncoder (& shard .buf )
400
388
}
389
+ blen := len (shard .buf )
401
390
if err := fut .pack (& shard .buf , shard .enc , body ); err != nil {
391
+ shard .buf = shard .buf [:blen ]
402
392
fut .err = err
403
- shard .Unlock ()
393
+ shard .bufmut .Unlock ()
394
+ return
395
+ }
396
+ shard .rmut .Lock ()
397
+ if conn .closed {
398
+ shard .buf = shard .buf [:blen ]
399
+ shard .bufmut .Unlock ()
400
+ shard .rmut .Unlock ()
401
+ fut .err = ClientError {ErrConnectionClosed , "using closed connection" }
404
402
return
405
403
}
404
+ shard .bufmut .Unlock ()
406
405
pos := (fut .requestId / shards ) & (requestsMap - 1 )
407
406
fut .next = shard .requests [pos ]
408
407
shard .requests [pos ] = fut
@@ -412,17 +411,19 @@ func (conn *Connection) putFuture(fut *Future, body func(*msgpack.Encoder) error
412
411
* shard .last = fut
413
412
shard .last = & fut .time .next
414
413
}
415
- atomic .AddUint32 (& shard .count , 1 )
416
- shard .Unlock ()
414
+ shard .rmut .Unlock ()
415
+ if firstWritten {
416
+ conn .dirtyShard <- shardn
417
+ }
417
418
}
418
419
419
- func (conn * Connection ) unlinkFutureTime (shard uint32 , fut * Future ) {
420
+ func (conn * Connection ) unlinkFutureTime (fut * Future ) {
420
421
if fut .time .prev != nil {
421
- i := fut .requestId & (shards - 1 )
422
422
* fut .time .prev = fut .time .next
423
423
if fut .time .next != nil {
424
424
fut .time .next .time .prev = fut .time .prev
425
425
} else {
426
+ i := fut .requestId & (shards - 1 )
426
427
conn .shard [i ].last = fut .time .prev
427
428
}
428
429
fut .time .next = nil
@@ -431,33 +432,34 @@ func (conn *Connection) unlinkFutureTime(shard uint32, fut *Future) {
431
432
}
432
433
433
434
func (conn * Connection ) fetchFuture (reqid uint32 ) (fut * Future ) {
434
- conn .shard [reqid & (shards - 1 )].Lock ()
435
+ shard := & conn .shard [reqid & (shards - 1 )]
436
+ shard .rmut .Lock ()
435
437
fut = conn .fetchFutureImp (reqid )
436
- conn . shard [ reqid & ( shards - 1 )] .Unlock ()
438
+ shard . rmut .Unlock ()
437
439
return fut
438
440
}
439
441
440
442
func (conn * Connection ) fetchFutureImp (reqid uint32 ) * Future {
441
- shardn := reqid & (shards - 1 )
442
- shard := & conn .shard [shardn ]
443
+ shard := & conn .shard [reqid & (shards - 1 )]
443
444
pos := (reqid / shards ) & (requestsMap - 1 )
444
445
fut := shard .requests [pos ]
445
446
if fut == nil {
446
447
return nil
447
448
}
448
449
if fut .requestId == reqid {
449
450
shard .requests [pos ] = fut .next
450
- conn .unlinkFutureTime (shardn , fut )
451
+ conn .unlinkFutureTime (fut )
451
452
return fut
452
453
}
453
454
for fut .next != nil {
454
- if fut . next . requestId == reqid {
455
- fut , fut . next = fut . next , fut . next . next
456
- conn . unlinkFutureTime ( shardn , fut )
455
+ next := fut . next
456
+ if next . requestId == reqid {
457
+ fut , fut . next = next , next . next
457
458
fut .next = nil
459
+ conn .unlinkFutureTime (fut )
458
460
return fut
459
461
}
460
- fut = fut . next
462
+ fut = next
461
463
}
462
464
return nil
463
465
}
@@ -479,17 +481,18 @@ func (conn *Connection) timeouts() {
479
481
}
480
482
minNext := nowepoch + timeout
481
483
for i := range conn .shard {
482
- conn .shard [i ].Lock ()
483
- for conn .shard [i ].first != nil && conn .shard [i ].first .timeout < nowepoch {
484
- fut := conn .shard [i ].first
485
- conn .shard [i ].Unlock ()
484
+ shard := & conn .shard [i ]
485
+ shard .rmut .Lock ()
486
+ for shard .first != nil && shard .first .timeout < nowepoch {
487
+ fut := shard .first
488
+ shard .rmut .Unlock ()
486
489
fut .timeouted ()
487
- conn . shard [ i ] .Lock ()
490
+ shard . rmut .Lock ()
488
491
}
489
- if conn . shard [ i ] .first != nil && conn . shard [ i ] .first .timeout < minNext {
490
- minNext = conn . shard [ i ] .first .timeout
492
+ if shard .first != nil && shard .first .timeout < minNext {
493
+ minNext = shard .first .timeout
491
494
}
492
- conn . shard [ i ] .Unlock ()
495
+ shard . rmut .Unlock ()
493
496
}
494
497
t .Reset (minNext - time .Now ().Sub (epoch ))
495
498
}
0 commit comments