@@ -163,8 +163,9 @@ type Greeting struct {
163
163
164
164
// Opts is a way to configure Connection
165
165
type Opts struct {
166
- // Timeout for any particular request. If Timeout is zero request, any
167
- // request can be blocked infinitely.
166
+ // Timeout for response to a particular request. The timeout is reset when
167
+ // push messages are received. If Timeout is zero, any request can be
168
+ // blocked infinitely.
168
169
// Also used to setup net.TCPConn.Set(Read|Write)Deadline.
169
170
Timeout time.Duration
170
171
// Timeout between reconnect attempts. If Reconnect is zero, no
@@ -568,8 +569,8 @@ func (conn *Connection) closeConnection(neterr error, forever bool) (err error)
568
569
requests [pos ].first = nil
569
570
requests [pos ].last = & requests [pos ].first
570
571
for fut != nil {
571
- fut .err = neterr
572
- fut . markReady ( conn )
572
+ fut .SetError ( neterr )
573
+ conn . markDone ( fut )
573
574
fut , fut .next = fut .next , nil
574
575
}
575
576
}
@@ -685,40 +686,61 @@ func (conn *Connection) reader(r *bufio.Reader, c net.Conn) {
685
686
conn .reconnect (err , c )
686
687
return
687
688
}
688
- if fut := conn .fetchFuture (resp .RequestId ); fut != nil {
689
- fut .resp = resp
690
- fut .markReady (conn )
689
+
690
+ var fut * Future = nil
691
+ if resp .Code == PushCode {
692
+ if fut = conn .peekFuture (resp .RequestId ); fut != nil {
693
+ fut .AppendPush (resp )
694
+ }
691
695
} else {
696
+ if fut = conn .fetchFuture (resp .RequestId ); fut != nil {
697
+ fut .SetResponse (resp )
698
+ conn .markDone (fut )
699
+ }
700
+ }
701
+ if fut == nil {
692
702
conn .opts .Logger .Report (LogUnexpectedResultId , conn , resp )
693
703
}
694
704
}
695
705
}
696
706
697
707
func (conn * Connection ) newFuture (requestCode int32 ) (fut * Future ) {
698
- fut = & Future {}
708
+ fut = NewFuture ()
699
709
if conn .rlimit != nil && conn .opts .RLimitAction == RLimitDrop {
700
710
select {
701
711
case conn .rlimit <- struct {}{}:
702
712
default :
703
- fut .err = ClientError {ErrRateLimited , "Request is rate limited on client" }
713
+ fut .err = ClientError {
714
+ ErrRateLimited ,
715
+ "Request is rate limited on client" ,
716
+ }
717
+ fut .ready = nil
718
+ fut .done = nil
704
719
return
705
720
}
706
721
}
707
- fut .ready = make (chan struct {})
708
722
fut .requestId = conn .nextRequestId ()
709
723
fut .requestCode = requestCode
710
724
shardn := fut .requestId & (conn .opts .Concurrency - 1 )
711
725
shard := & conn .shard [shardn ]
712
726
shard .rmut .Lock ()
713
727
switch conn .state {
714
728
case connClosed :
715
- fut .err = ClientError {ErrConnectionClosed , "using closed connection" }
729
+ fut .err = ClientError {
730
+ ErrConnectionClosed ,
731
+ "using closed connection" ,
732
+ }
716
733
fut .ready = nil
734
+ fut .done = nil
717
735
shard .rmut .Unlock ()
718
736
return
719
737
case connDisconnected :
720
- fut .err = ClientError {ErrConnectionNotReady , "client connection is not ready" }
738
+ fut .err = ClientError {
739
+ ErrConnectionNotReady ,
740
+ "client connection is not ready" ,
741
+ }
721
742
fut .ready = nil
743
+ fut .done = nil
722
744
shard .rmut .Unlock ()
723
745
return
724
746
}
@@ -737,22 +759,38 @@ func (conn *Connection) newFuture(requestCode int32) (fut *Future) {
737
759
runtime .Gosched ()
738
760
select {
739
761
case conn .rlimit <- struct {}{}:
740
- case <- fut .ready :
762
+ case <- fut .done :
741
763
if fut .err == nil {
742
- panic ("fut.ready is closed, but err is nil" )
764
+ panic ("fut.done is closed, but err is nil" )
743
765
}
744
766
}
745
767
}
746
768
}
747
769
return
748
770
}
749
771
772
+ func (conn * Connection ) sendFuture (fut * Future , body func (* msgpack.Encoder ) error ) * Future {
773
+ if fut .ready == nil {
774
+ return fut
775
+ }
776
+ conn .putFuture (fut , body )
777
+ return fut
778
+ }
779
+
780
+ func (conn * Connection ) failFuture (fut * Future , err error ) * Future {
781
+ if f := conn .fetchFuture (fut .requestId ); f == fut {
782
+ fut .SetError (err )
783
+ conn .markDone (fut )
784
+ }
785
+ return fut
786
+ }
787
+
750
788
func (conn * Connection ) putFuture (fut * Future , body func (* msgpack.Encoder ) error ) {
751
789
shardn := fut .requestId & (conn .opts .Concurrency - 1 )
752
790
shard := & conn .shard [shardn ]
753
791
shard .bufmut .Lock ()
754
792
select {
755
- case <- fut .ready :
793
+ case <- fut .done :
756
794
shard .bufmut .Unlock ()
757
795
return
758
796
default :
@@ -767,8 +805,8 @@ func (conn *Connection) putFuture(fut *Future, body func(*msgpack.Encoder) error
767
805
shard .buf .Trunc (blen )
768
806
shard .bufmut .Unlock ()
769
807
if f := conn .fetchFuture (fut .requestId ); f == fut {
770
- fut .markReady ( conn )
771
- fut . err = err
808
+ fut .SetError ( err )
809
+ conn . markDone ( fut )
772
810
} else if f != nil {
773
811
/* in theory, it is possible. In practice, you have
774
812
* to have race condition that lasts hours */
@@ -782,7 +820,7 @@ func (conn *Connection) putFuture(fut *Future, body func(*msgpack.Encoder) error
782
820
// packing error is more important than connection
783
821
// error, because it is indication of programmer's
784
822
// mistake.
785
- fut .err = err
823
+ fut .SetError ( err )
786
824
}
787
825
}
788
826
return
@@ -793,15 +831,40 @@ func (conn *Connection) putFuture(fut *Future, body func(*msgpack.Encoder) error
793
831
}
794
832
}
795
833
834
+ func (conn * Connection ) markDone (fut * Future ) {
835
+ if conn .rlimit != nil {
836
+ <- conn .rlimit
837
+ }
838
+ }
839
+
840
+ func (conn * Connection ) peekFuture (reqid uint32 ) (fut * Future ) {
841
+ shard := & conn .shard [reqid & (conn .opts .Concurrency - 1 )]
842
+ pos := (reqid / conn .opts .Concurrency ) & (requestsMap - 1 )
843
+ shard .rmut .Lock ()
844
+ defer shard .rmut .Unlock ()
845
+
846
+ if conn .opts .Timeout > 0 {
847
+ fut = conn .getFutureImp (reqid , true )
848
+ pair := & shard .requests [pos ]
849
+ * pair .last = fut
850
+ pair .last = & fut .next
851
+ fut .timeout = time .Since (epoch ) + conn .opts .Timeout
852
+ } else {
853
+ fut = conn .getFutureImp (reqid , false )
854
+ }
855
+
856
+ return fut
857
+ }
858
+
796
859
func (conn * Connection ) fetchFuture (reqid uint32 ) (fut * Future ) {
797
860
shard := & conn .shard [reqid & (conn .opts .Concurrency - 1 )]
798
861
shard .rmut .Lock ()
799
- fut = conn .fetchFutureImp (reqid )
862
+ fut = conn .getFutureImp (reqid , true )
800
863
shard .rmut .Unlock ()
801
864
return fut
802
865
}
803
866
804
- func (conn * Connection ) fetchFutureImp (reqid uint32 ) * Future {
867
+ func (conn * Connection ) getFutureImp (reqid uint32 , fetch bool ) * Future {
805
868
shard := & conn .shard [reqid & (conn .opts .Concurrency - 1 )]
806
869
pos := (reqid / conn .opts .Concurrency ) & (requestsMap - 1 )
807
870
pair := & shard .requests [pos ]
@@ -812,11 +875,13 @@ func (conn *Connection) fetchFutureImp(reqid uint32) *Future {
812
875
return nil
813
876
}
814
877
if fut .requestId == reqid {
815
- * root = fut .next
816
- if fut .next == nil {
817
- pair .last = root
818
- } else {
819
- fut .next = nil
878
+ if fetch {
879
+ * root = fut .next
880
+ if fut .next == nil {
881
+ pair .last = root
882
+ } else {
883
+ fut .next = nil
884
+ }
820
885
}
821
886
return fut
822
887
}
@@ -851,11 +916,11 @@ func (conn *Connection) timeouts() {
851
916
} else {
852
917
fut .next = nil
853
918
}
854
- fut .err = ClientError {
919
+ fut .SetError ( ClientError {
855
920
Code : ErrTimeouted ,
856
921
Msg : fmt .Sprintf ("client timeout for request %d" , fut .requestId ),
857
- }
858
- fut . markReady ( conn )
922
+ })
923
+ conn . markDone ( fut )
859
924
shard .bufmut .Unlock ()
860
925
}
861
926
if pair .first != nil && pair .first .timeout < minNext {
0 commit comments