@@ -9,15 +9,25 @@ package comm
99import (
1010 "context"
1111 "sync"
12+ "sync/atomic"
1213
14+ "github.com/hyperledger-labs/fabric-smart-client/pkg/utils/errors"
15+ "github.com/hyperledger-labs/fabric-smart-client/pkg/utils/proto"
1316 "github.com/hyperledger-labs/fabric-smart-client/platform/view/services/comm/host"
1417 "github.com/hyperledger-labs/fabric-smart-client/platform/view/view"
1518 "go.uber.org/zap/zapcore"
1619)
1720
21+ // ErrSessionClosed is returned when a message is sent when the session is closed.
22+ var ErrSessionClosed = errors .New ("session closed" )
23+
24+ type sender interface {
25+ sendTo (ctx context.Context , info host.StreamInfo , msg proto.Message ) error
26+ }
27+
1828// NetworkStreamSession implements view.Session
1929type NetworkStreamSession struct {
20- node * P2PNode
30+ node sender
2131 endpointID []byte
2232 endpointAddress string
2333 contextID string
@@ -26,10 +36,16 @@ type NetworkStreamSession struct {
2636 callerViewID string
2737 incoming chan * view.Message
2838 streams map [* streamHandler ]struct {}
29- closed bool
3039 mutex sync.RWMutex
40+
41+ closed atomic.Bool
42+ closeOnce sync.Once
43+ ctx context.Context
44+ cancel context.CancelFunc
45+ wg sync.WaitGroup
3146}
3247
48+ // Info returns a view.SessionInfo.
3349func (n * NetworkStreamSession ) Info () view.SessionInfo {
3450 n .mutex .RLock ()
3551 defer n .mutex .RUnlock ()
@@ -39,25 +55,27 @@ func (n *NetworkStreamSession) Info() view.SessionInfo {
3955 CallerViewID : n .callerViewID ,
4056 Endpoint : n .endpointAddress ,
4157 EndpointPKID : n .endpointID ,
42- Closed : n .closed ,
58+ Closed : n .isClosed () ,
4359 }
4460 return ret
4561}
4662
47- // Send sends the payload to the endpoint
63+ // Send sends the payload to the endpoint.
4864func (n * NetworkStreamSession ) Send (payload []byte ) error {
4965 return n .SendWithContext (context .TODO (), payload )
5066}
5167
68+ // SendWithContext sends the payload to the endpoint with the passed context.Context.
5269func (n * NetworkStreamSession ) SendWithContext (ctx context.Context , payload []byte ) error {
5370 return n .sendWithStatus (ctx , payload , view .OK )
5471}
5572
56- // SendError sends an error to the endpoint with the passed payload
73+ // SendError sends an error to the endpoint with the passed payload.
5774func (n * NetworkStreamSession ) SendError (payload []byte ) error {
5875 return n .SendErrorWithContext (context .TODO (), payload )
5976}
6077
78+ // SendErrorWithContext sends an error to the endpoint with the passed context.Context and payload.
6179func (n * NetworkStreamSession ) SendErrorWithContext (ctx context.Context , payload []byte ) error {
6280 return n .sendWithStatus (ctx , payload , view .ERROR )
6381}
@@ -67,45 +85,77 @@ func (n *NetworkStreamSession) Receive() <-chan *view.Message {
6785 return n .incoming
6886}
6987
88+ // enqueue enqueues a message into the session's incoming channel.
89+ // If the session is closed, the message will be dropped and a warning is logged.
90+ func (n * NetworkStreamSession ) enqueue (msg * view.Message ) {
91+ if msg == nil {
92+ return
93+ }
94+
95+ if n .isClosed () {
96+ logger .Warnf ("dropping message from %s for closed session [%s]" , msg .Caller , msg .SessionID )
97+ return
98+ }
99+
100+ n .wg .Add (1 )
101+ defer n .wg .Done ()
102+
103+ select {
104+ case <- n .ctx .Done ():
105+ logger .Warnf ("dropping message from %s for closed session [%s]" , msg .Caller , msg .SessionID )
106+ return
107+ case n .incoming <- msg :
108+ }
109+ }
110+
70111// Close releases all the resources allocated by this session
71112func (n * NetworkStreamSession ) Close () {
72- n .node .sessionsMutex .Lock ()
73- defer n .node .sessionsMutex .Unlock ()
74-
75113 n .closeInternal ()
76114}
77115
78116func (n * NetworkStreamSession ) closeInternal () {
79- if n .closed {
80- return
81- }
82-
83- logger .Debugf ("closing session [%s] with [%d] streams" , n .sessionID , len (n .streams ))
84- toClose := make ([]* streamHandler , 0 , len (n .streams ))
85- for stream := range n .streams {
86- if logger .IsEnabledFor (zapcore .DebugLevel ) {
87- logger .Debugf ("session [%s], stream [%s], refCtr [%d]" , n .sessionID , stream .stream .Hash (), stream .refCtr )
117+ n .closeOnce .Do (func () {
118+ n .mutex .Lock ()
119+ defer n .mutex .Unlock ()
120+
121+ logger .Debugf ("closing session [%s] with [%d] streams" , n .sessionID , len (n .streams ))
122+ toClose := make ([]* streamHandler , 0 , len (n .streams ))
123+ for stream := range n .streams {
124+ if logger .IsEnabledFor (zapcore .DebugLevel ) {
125+ logger .Debugf ("session [%s], stream [%s], refCtr [%d]" , n .sessionID , stream .stream .Hash (), stream .refCtr )
126+ }
127+ stream .refCtr --
128+ if stream .refCtr == 0 {
129+ toClose = append (toClose , stream )
130+ }
88131 }
89- stream .refCtr --
90- if stream .refCtr == 0 {
91- toClose = append (toClose , stream )
132+
133+ logger .Debugf ("closing session [%s]'s streams [%d]" , n .sessionID , len (toClose ))
134+ for _ , stream := range toClose {
135+ stream .close (context .TODO ())
92136 }
93- }
137+ logger . Debugf ( "closing session [%s]'s streams [%d] done" , n . sessionID , len ( toClose ))
94138
95- logger .Debugf ("closing session [%s]'s streams [%d]" , n .sessionID , len (toClose ))
96- for _ , stream := range toClose {
97- stream .close (context .TODO ())
98- }
139+ // next we are closing the incoming and the closing signal channel to drain the receivers;
140+ n .closed .Store (true )
141+ n .cancel ()
142+ n .wg .Wait ()
143+ close (n .incoming )
144+ n .streams = make (map [* streamHandler ]struct {})
99145
100- logger .Debugf ("closing session [%s]'s streams [%d] done" , n .sessionID , len (toClose ))
101- close (n .incoming )
102- n .closed = true
103- n .streams = make (map [* streamHandler ]struct {})
146+ logger .Debugf ("closing session [%s] done" , n .sessionID )
147+ })
148+ }
104149
105- logger .Debugf ("closing session [%s] done" , n .sessionID )
150+ func (n * NetworkStreamSession ) isClosed () bool {
151+ return n .closed .Load ()
106152}
107153
108154func (n * NetworkStreamSession ) sendWithStatus (ctx context.Context , payload []byte , status int32 ) error {
155+ if n .isClosed () {
156+ return ErrSessionClosed
157+ }
158+
109159 n .mutex .RLock ()
110160 info := host.StreamInfo {
111161 RemotePeerID : string (n .endpointID ),
@@ -123,11 +173,12 @@ func (n *NetworkStreamSession) sendWithStatus(ctx context.Context, payload []byt
123173 n .mutex .RUnlock ()
124174
125175 err := n .node .sendTo (ctx , info , packet )
126- logger .Debugf ("sent message [len:%d] to [%s:%s] from [%s] with err [%v]" ,
176+ logger .Debugf ("sent message [len:%d] to [%s:%s] from [%s] [status:%v] with err [%v]" ,
127177 len (payload ),
128178 info .RemotePeerID ,
129179 info .RemotePeerAddress ,
130180 packet .Caller ,
181+ status ,
131182 err ,
132183 )
133184 return err
0 commit comments