@@ -137,6 +137,11 @@ type Handler struct {
137137 // resource name will be used as identifier.
138138 Group string
139139
140+ // Parallel is a flag telling that all requests to the handler may be
141+ // handled in parallel on different goroutines. If set to true, any value in
142+ // Group will be ignored.
143+ Parallel bool
144+
140145 // OnRegister is callback that is to be call when the handler has been
141146 // registered to a service.
142147 //
@@ -196,7 +201,9 @@ type Service struct {
196201 nc Conn // NATS Server connection
197202 inCh chan * nats.Msg // Channel for incoming nats messages
198203 rwork map [string ]* work // map of resource work
199- workCh chan * work // Resource work channel, listened to by the workers
204+ workqueue []* work // Resource work queue.
205+ workbuf []* work // Underlying buffer of the workqueue
206+ workcond sync.Cond // Cond waited on by workers and signaled when work is added to workqueue
200207 wg sync.WaitGroup // WaitGroup for all workers
201208 mu sync.Mutex // Mutex to protect rwork map
202209 logger logger.Logger // Logger
@@ -517,6 +524,16 @@ func Group(group string) Option {
517524 })
518525}
519526
527+ // Parallel sets the parallel flag. All requests for the handler may be handled
528+ // in parallel on different worker goroutines.
529+ //
530+ // If set to true, any value in Group will be ignored.
531+ func Parallel (parallel bool ) Option {
532+ return OptionFunc (func (hs * Handler ) {
533+ hs .Parallel = parallel
534+ })
535+ }
536+
520537// OnRegister sets a callback to be called when the handler is registered to a
521538// service.
522539//
@@ -648,14 +665,16 @@ func (s *Service) serve(nc Conn) error {
648665 workCh := make (chan * work , 1 )
649666 s .nc = nc
650667 s .inCh = inCh
651- s .workCh = workCh
652- s .rwork = make (map [string ]* work )
668+ s .workcond = sync.Cond {L : & s .mu }
669+ s .workbuf = make ([]* work , s .inChannelSize )
670+ s .workqueue = s .workbuf [:0 ]
671+ s .rwork = make (map [string ]* work , s .inChannelSize )
653672 s .queryTQ = timerqueue .New (s .queryEventExpire , s .queryDuration )
654673
655674 // Start workers
656675 s .wg .Add (s .workerCount )
657676 for i := 0 ; i < s .workerCount ; i ++ {
658- go s .startWorker (s . workCh )
677+ go s .startWorker ()
659678 }
660679
661680 atomic .StoreInt32 (& s .state , stateStarted )
@@ -699,7 +718,6 @@ func (s *Service) Shutdown() error {
699718
700719 s .inCh = nil
701720 s .nc = nil
702- s .workCh = nil
703721
704722 atomic .StoreInt32 (& s .state , stateStopped )
705723
@@ -709,6 +727,11 @@ func (s *Service) Shutdown() error {
709727
710728// close calls Close on the NATS connection, and closes the incoming channel
711729func (s * Service ) close () {
730+ s .mu .Lock ()
731+ s .workqueue = nil
732+ s .mu .Unlock ()
733+ s .workcond .Broadcast ()
734+
712735 s .nc .Close ()
713736 close (s .inCh )
714737}
@@ -956,17 +979,25 @@ func (s *Service) runWith(wid string, cb func()) {
956979
957980 s .mu .Lock ()
958981 // Get current work queue for the resource
959- w , ok := s .rwork [wid ]
982+ var w * work
983+ var ok bool
984+ if wid != "" {
985+ w , ok = s .rwork [wid ]
986+ }
960987 if ! ok {
961988 // Create a new work queue and pass it to a worker
962989 w = & work {
963- s : s ,
964- wid : wid ,
965- queue : []func (){cb },
990+ s : s ,
991+ wid : wid ,
992+ single : [1 ]func (){cb },
993+ }
994+ w .queue = w .single [:1 ]
995+ if wid != "" {
996+ s .rwork [wid ] = w
966997 }
967- s .rwork [ wid ] = w
998+ s .workqueue = append ( s . workqueue , w )
968999 s .mu .Unlock ()
969- s .workCh <- w
1000+ s .workcond . Signal ()
9701001 } else {
9711002 // Append callback to existing work queue
9721003 w .queue = append (w .queue , cb )
0 commit comments