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

     1  package goseq
     2  
     3  import (
     4  	"time"
     5  )
     6  
     7  const (
     8  	initialTasksCap = 4
     9  )
    10  
    11  // This type is defined acceptable function. 1st argument is to set
    12  // a current SequenceID and 2nd 'index' value is to generate from
    13  // 'id' value. index is between 0 and (size -1).
    14  type TaskHandler func(id SequenceID, index int)
    15  
    16  // Manage several TaskHandlers.
    17  // Create a new instance using NewTaskManager() and then
    18  // add TaskHandlers. And then, call Start() method to setup
    19  // channels for each handlers. After that, call PutTask
    20  // to initiate a new SequenceID and run tasks.
    21  // Current version can support a single thread to call Put method.
    22  type TaskManager interface {
    23  	Put(initHandler TaskHandler) SequenceID
    24  	AddHandler(handler TaskHandler, handlers ...TaskHandler) HandlerGroup
    25  	AddHandlers(handlers []TaskHandler) HandlerGroup
    26  	Start()
    27  	Stop()
    28  }
    29  
    30  type sequenceIDToIndexFunc func(id SequenceID) (index int)
    31  
    32  type taskManager struct {
    33  	seqToIndexFunc      sequenceIDToIndexFunc
    34  	handlerGroups       []HandlerGroup
    35  	size                SequenceID
    36  	indexMask           SequenceID
    37  	currentID           SequenceID
    38  	cachedMinSequenceID SequenceID
    39  }
    40  
    41  // Create a new TaskManager instance.
    42  // size is required to set 2^x like 2,4,8,16, ...
    43  func NewTaskManager(size int) TaskManager {
    44  	return newTaskManager(size)
    45  }
    46  
    47  func newTaskManager(size int) (tm *taskManager) {
    48  	tm = new(taskManager)
    49  	tm.currentID = initialSequenceValue
    50  	tm.cachedMinSequenceID = initialSequenceValue
    51  	tm.size = SequenceID(size)
    52  	tm.indexMask = SequenceID(size - 1)
    53  	tm.handlerGroups = make([]HandlerGroup, 0, initialTasksCap)
    54  	tm.seqToIndexFunc = func(id SequenceID) int {
    55  		return int(tm.indexMask & id)
    56  	}
    57  	return
    58  }
    59  
    60  // This method gets a new SequenceID and then call initHandler to initialize
    61  // for the new SequenceID/index parameters. And then start calling TaskHandlers
    62  // in different Goroutine. Current method can support only for a single thread
    63  // usage to call this method. If 'index' is still used, then this method
    64  // block the call until 'index' becomes available state.
    65  func (tm *taskManager) Put(initHandler TaskHandler) SequenceID {
    66  	current := tm.currentID
    67  	nextID := current + 1
    68  	wrapPoint := nextID - tm.size
    69  	cachedMinSequenceID := tm.cachedMinSequenceID
    70  
    71  	if wrapPoint > cachedMinSequenceID || cachedMinSequenceID > current {
    72  		var minSequenceID SequenceID
    73  		for {
    74  			minSequenceID := tm.getMinimumLastProcessedID(current)
    75  			if wrapPoint > minSequenceID {
    76  				time.Sleep(1)
    77  			} else {
    78  				break
    79  			}
    80  		}
    81  		tm.cachedMinSequenceID = minSequenceID
    82  	}
    83  	tm.currentID = nextID
    84  
    85  	defer tm.put(nextID)
    86  
    87  	if initHandler != nil {
    88  		initHandler(nextID, tm.seqToIndexFunc(nextID))
    89  	}
    90  	return nextID
    91  }
    92  
    93  // This can support a single thread operation only because
    94  // multi thread call may put different order.
    95  func (tm *taskManager) put(id SequenceID) {
    96  	for _, group := range tm.handlerGroups {
    97  		group.process(id)
    98  	}
    99  }
   100  
   101  func (tm *taskManager) AddHandler(handler TaskHandler, handlers ...TaskHandler) HandlerGroup {
   102  	group := newHandlerGroup(tm.seqToIndexFunc)
   103  	if handler != nil {
   104  		group.AddHandler(handler)
   105  	}
   106  	if len(handlers) > 0 {
   107  		group.AddHandlers(handlers)
   108  	}
   109  	tm.handlerGroups = append(tm.handlerGroups, group)
   110  	return group
   111  }
   112  
   113  func (tm *taskManager) AddHandlers(handlers []TaskHandler) HandlerGroup {
   114  	return tm.AddHandler(nil, handlers...)
   115  }
   116  
   117  // Start all configured channels. Don't add new handlers/groups after starting handlers.
   118  func (tm *taskManager) Start() {
   119  	for _, group := range tm.handlerGroups {
   120  		group.startAll()
   121  	}
   122  }
   123  
   124  // Stop all configured channels. This is blocked until finishing all goroutines.
   125  func (tm *taskManager) Stop() {
   126  	for _, group := range tm.handlerGroups {
   127  		group.stopAll()
   128  	}
   129  }
   130  
   131  func (tm *taskManager) getMinimumLastProcessedID(minimum SequenceID) SequenceID {
   132  	processedID := minimum
   133  	for _, handlerGroup := range tm.handlerGroups {
   134  		for _, lastGroup := range handlerGroup.lastHandlerGroups() {
   135  			n := lastGroup.LastProcessedID()
   136  			if n < processedID {
   137  				processedID = n
   138  			}
   139  		}
   140  	}
   141  	return processedID
   142  }