github.com/hata/goseq@v0.0.0-20150316021154-a5ca66a92399/handlergroup.go (about)

     1  /*
     2     Package sequence is to run small tasks sequencially using Goroutine.
     3  
     4     The current version of Go language cannot use generic type without type
     5     assertion. So, this library doesn't manage efficient usage for context
     6     instances. Instead of using a context instance, this library provides
     7     'index' value to use a cache index. The provided index is
     8     generated based on configured 'size' parameter for TaskManager and
     9     it is reused after finishing a previous task which uses 'index'.
    10  */
    11  package goseq
    12  
    13  import (
    14  	"runtime"
    15  	"sync"
    16  )
    17  
    18  const (
    19  	channelBufferSize           = 512
    20  	stopCurrentHandlerGroupOnly = -1
    21  	stopAllHandlerGroups        = -2
    22  )
    23  
    24  // HandlerGroup is to manage several TaskHandler instances.
    25  // Added TaskHandlers are run at the same time when a new
    26  // SequenceID is put. After finishing all tasks for the SequenceID,
    27  // then next HandlerGroups are received the finished SequenceID.
    28  type HandlerGroup interface {
    29  	AddHandler(handler TaskHandler, handlers ...TaskHandler)
    30  	AddHandlers(handlers []TaskHandler)
    31  	Then(handler TaskHandler, handlers ...TaskHandler) HandlerGroup
    32  	LastProcessedID() SequenceID
    33  
    34  	start()
    35  	stop()
    36  	startAll()
    37  	stopAll()
    38  	waitStop()
    39  	waitStopAll()
    40  
    41  	process(id SequenceID)
    42  	addNextGroup(nextGroup HandlerGroup)
    43  	addNextGroups(nextGroup HandlerGroup, nextGroups ...HandlerGroup)
    44  
    45  	lastHandlerGroups() []HandlerGroup
    46  
    47  	numOfHandlers() int
    48  }
    49  
    50  type handlerGroup struct {
    51  	name            string
    52  	nextGroups      []HandlerGroup
    53  	handlers        []TaskHandler
    54  	inChannels      []chan SequenceID
    55  	outChannels     []chan SequenceID
    56  	lastProcessedID Sequence
    57  	seqToIndexFunc  sequenceIDToIndexFunc
    58  	waitingStart    sync.WaitGroup
    59  	waitingStop     sync.WaitGroup
    60  }
    61  
    62  func newHandlerGroup(toIndexFunc sequenceIDToIndexFunc) (group *handlerGroup) {
    63  	group = new(handlerGroup)
    64  	group.handlers = make([]TaskHandler, 0, 2)
    65  	group.nextGroups = make([]HandlerGroup, 0, 2)
    66  	group.lastProcessedID = NewSequence()
    67  	group.seqToIndexFunc = toIndexFunc
    68  	return
    69  }
    70  
    71  func (group *handlerGroup) process(id SequenceID) {
    72  	for _, ch := range group.inChannels {
    73  		ch <- id
    74  	}
    75  }
    76  
    77  // Add a TaskHandler or some TaskHandlers.
    78  func (group *handlerGroup) AddHandler(handler TaskHandler, handlers ...TaskHandler) {
    79  	group.handlers = append(group.handlers, handler)
    80  	if len(handlers) > 0 {
    81  		group.handlers = append(group.handlers, handlers...)
    82  	}
    83  }
    84  
    85  // Add TaskHandlers from an slice of TaskHandler.
    86  func (group *handlerGroup) AddHandlers(handlers []TaskHandler) {
    87  	if handlers != nil {
    88  		group.handlers = append(group.handlers, handlers...)
    89  	}
    90  }
    91  
    92  func (group *handlerGroup) addNextGroup(nextGroup HandlerGroup) {
    93  	group.nextGroups = append(group.nextGroups, nextGroup)
    94  }
    95  
    96  func (group *handlerGroup) addNextGroups(nextGroup HandlerGroup, nextGroups ...HandlerGroup) {
    97  	group.nextGroups = append(group.nextGroups, nextGroup)
    98  	if nextGroups != nil {
    99  		group.nextGroups = append(group.nextGroups, nextGroups...)
   100  	}
   101  }
   102  
   103  func (group *handlerGroup) processHandler(handler TaskHandler, inChannel <-chan SequenceID, outChannel chan<- SequenceID) {
   104  	group.waitingStart.Done()
   105  	defer group.waitingStop.Done()
   106  	for {
   107  		id := <-inChannel
   108  		if id == stopCurrentHandlerGroupOnly || id == stopAllHandlerGroups {
   109  			outChannel <- id
   110  			break
   111  		}
   112  		handler(id, group.seqToIndexFunc(id))
   113  		outChannel <- id
   114  		runtime.Gosched()
   115  	}
   116  }
   117  
   118  func (group *handlerGroup) sendToNextGroups() {
   119  	group.waitingStart.Done()
   120  	defer group.waitingStop.Done()
   121  	var id SequenceID
   122  	if len(group.outChannels) > 0 {
   123  		for {
   124  			// This loop expects all ids are the same.
   125  			for _, ch := range group.outChannels {
   126  				id = <-ch
   127  			}
   128  			if id == stopCurrentHandlerGroupOnly {
   129  				break
   130  			}
   131  			for _, nextGroup := range group.nextGroups {
   132  				nextGroup.process(id)
   133  			}
   134  			if id == stopAllHandlerGroups {
   135  				break
   136  			}
   137  			group.lastProcessedID.Set(id)
   138  		}
   139  	}
   140  }
   141  
   142  func (group *handlerGroup) start() {
   143  	length := len(group.handlers)
   144  	group.waitingStart.Add(length + 1)
   145  	group.waitingStop.Add(length + 1)
   146  	group.inChannels = make([]chan SequenceID, length)
   147  	group.outChannels = make([]chan SequenceID, length)
   148  
   149  	for i, handler := range group.handlers {
   150  		group.inChannels[i] = make(chan SequenceID, channelBufferSize)
   151  		group.outChannels[i] = make(chan SequenceID, channelBufferSize)
   152  		go group.processHandler(handler, group.inChannels[i], group.outChannels[i])
   153  	}
   154  
   155  	go group.sendToNextGroups()
   156  	group.waitingStart.Wait()
   157  }
   158  
   159  func (group *handlerGroup) startAll() {
   160  	for _, nextGroup := range group.nextGroups {
   161  		nextGroup.startAll()
   162  	}
   163  	group.start()
   164  }
   165  
   166  func (group *handlerGroup) stop() {
   167  	group.process(stopCurrentHandlerGroupOnly)
   168  	group.waitStop()
   169  }
   170  
   171  func (group *handlerGroup) stopAll() {
   172  	group.process(stopAllHandlerGroups)
   173  	group.waitStopAll()
   174  }
   175  
   176  func (group *handlerGroup) waitStop() {
   177  	group.waitingStop.Wait()
   178  	defer func() {
   179  		group.inChannels = nil
   180  		group.outChannels = nil
   181  	}()
   182  
   183  	for _, ch := range group.inChannels {
   184  		close(ch)
   185  	}
   186  	for _, ch := range group.outChannels {
   187  		close(ch)
   188  	}
   189  }
   190  
   191  func (group *handlerGroup) waitStopAll() {
   192  	group.waitStop()
   193  	for _, nextGroup := range group.nextGroups {
   194  		nextGroup.waitStopAll()
   195  	}
   196  }
   197  
   198  func (group *handlerGroup) lastHandlerGroups() []HandlerGroup {
   199  	groups := make([]HandlerGroup, 0, len(group.nextGroups))
   200  	if len(group.nextGroups) == 0 {
   201  		groups = append(groups, group)
   202  	} else {
   203  		for _, next := range group.nextGroups {
   204  			groups = append(groups, next.lastHandlerGroups()...)
   205  		}
   206  	}
   207  	return groups
   208  }
   209  
   210  func (group *handlerGroup) numOfHandlers() int {
   211  	return len(group.handlers)
   212  }
   213  
   214  // Create a new HandlerGroup and then add new TaskHandler instances to the
   215  // new HandlerGroup. And then return the new handler. This new added handlers
   216  // are run after running current(group instance) HandlerGroup's TaskHandlers.
   217  func (group *handlerGroup) Then(handler TaskHandler, handlers ...TaskHandler) HandlerGroup {
   218  	newGroup := newHandlerGroup(group.seqToIndexFunc)
   219  	newGroup.AddHandler(handler, handlers...)
   220  	group.addNextGroups(newGroup)
   221  	return newGroup
   222  }
   223  
   224  // Get finished SequenceID. The returned value means that all tasks are finished
   225  // for the specific returned value or more smaller SequenceIDs.
   226  func (group *handlerGroup) LastProcessedID() SequenceID {
   227  	return group.lastProcessedID.Get()
   228  }