github.com/searKing/golang/go@v1.2.74/container/stream/op/terminal/short_circuit_task.go (about)

     1  // Copyright 2020 The searKing Author. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package terminal
     6  
     7  import (
     8  	"context"
     9  	"sync/atomic"
    10  
    11  	"github.com/searKing/golang/go/error/exception"
    12  	"github.com/searKing/golang/go/util/spliterator"
    13  )
    14  
    15  //go:generate go-atomicvalue -type "sharedResult<Sink>"
    16  type sharedResult atomic.Value
    17  
    18  /**
    19   * Abstract class for fork-join tasks used to implement short-circuiting
    20   * stream ops, which can produce a result without processing all elements of the
    21   * stream.
    22   *
    23   * @param <P_IN> type of input elements to the pipeline
    24   * @param <P_OUT> type of output elements from the pipeline
    25   * @param <R> type of intermediate result, may be different from operation
    26   *        result type
    27   * @param <K> type of child and sibling tasks
    28   * @since 1.8
    29   */
    30  type ShortCircuitTask interface {
    31  	Task
    32  
    33  	SharedResult() *sharedResult
    34  	/**
    35  	 * The result for this computation; this is shared among all tasks and set
    36  	 * exactly once
    37  	 */
    38  	GetSharedResult() Sink
    39  
    40  	/**
    41  	 * Declares that a globally valid result has been found.  If another task has
    42  	 * not already found the answer, the result is installed in
    43  	 * {@code sharedResult}.  The {@code compute()} method will check
    44  	 * {@code sharedResult} before proceeding with computation, so this causes
    45  	 * the computation to terminate early.
    46  	 *
    47  	 * @param result the result found
    48  	 */
    49  	ShortCircuit(result Sink)
    50  
    51  	/**
    52  	 * Mark this task as canceled
    53  	 */
    54  	Cancel()
    55  
    56  	/**
    57  	 * Queries whether this task is canceled.  A task is considered canceled if
    58  	 * it or any of its parents have been canceled.
    59  	 *
    60  	 * @return {@code true} if this task or any parent is canceled.
    61  	 */
    62  	TaskCanceled() bool
    63  
    64  	/**
    65  	 * Cancels all tasks which succeed this one in the encounter order.  This
    66  	 * includes canceling all the current task's right sibling, as well as the
    67  	 * later right siblings of all its parents.
    68  	 */
    69  	CancelLaterNodes()
    70  }
    71  
    72  type TODOShortCircuitTask struct {
    73  	TODOTask
    74  
    75  	/**
    76  	 * The result for this computation; this is shared among all tasks and set
    77  	 * exactly once
    78  	 */
    79  	sharedResult *sharedResult
    80  
    81  	/**
    82  	 * Indicates whether this task has been canceled.  Tasks may cancel other
    83  	 * tasks in the computation under various conditions, such as in a
    84  	 * find-first operation, a task that finds a value will cancel all tasks
    85  	 * that are later in the encounter order.
    86  	 */
    87  	canceled bool
    88  }
    89  
    90  /**
    91   * Constructor for root tasks.
    92   *
    93   * @param helper the {@code PipelineHelper} describing the stream pipeline
    94   *               up to this operation
    95   * @param spliterator the {@code Spliterator} describing the source for this
    96   *                    pipeline
    97   */
    98  func (task *TODOShortCircuitTask) WithSpliterator(spliterator spliterator.Spliterator) *TODOShortCircuitTask {
    99  	task.TODOTask.WithSpliterator(spliterator)
   100  	task.sharedResult = &sharedResult{}
   101  	return task
   102  }
   103  
   104  func (task *TODOShortCircuitTask) SharedResult() *sharedResult {
   105  	return task.sharedResult
   106  }
   107  
   108  /**
   109   * Constructor for non-root nodes.
   110   *
   111   * @param parent parent task in the computation tree
   112   * @param spliterator the {@code Spliterator} for the portion of the
   113   *                    computation tree described by this task
   114   */
   115  func (task *TODOShortCircuitTask) WithParent(parent ShortCircuitTask, spliterator spliterator.Spliterator) *TODOShortCircuitTask {
   116  	task.TODOTask.WithParent(parent, spliterator)
   117  	task.sharedResult = parent.SharedResult()
   118  	task.SetDerived(task)
   119  	return task
   120  }
   121  
   122  // Helper
   123  
   124  /**
   125   * Overrides TODOTask version to include checks for early
   126   * exits while splitting or computing.
   127   */
   128  func (task *TODOShortCircuitTask) Compute(ctx context.Context) {
   129  	rs := task.spliterator
   130  	var ls spliterator.Spliterator
   131  	sizeEstimate := rs.EstimateSize()
   132  	sizeThreshold := task.getTargetSize(sizeEstimate)
   133  
   134  	var this = task.GetDerivedElse(task).(Task)
   135  	var forkRight bool
   136  	var sr = task.sharedResult
   137  	for sr.Load() == nil {
   138  		select {
   139  		case <-ctx.Done():
   140  			return
   141  		default:
   142  		}
   143  
   144  		if sizeEstimate <= sizeThreshold {
   145  			break
   146  		}
   147  
   148  		ls = rs.TrySplit()
   149  		if ls == nil {
   150  			break
   151  		}
   152  
   153  		var leftChild, rightChild, taskToFork Task
   154  		leftChild = this.MakeChild(ls)
   155  		rightChild = this.MakeChild(rs)
   156  		this.SetLeftChild(leftChild)
   157  		this.SetRightChild(rightChild)
   158  
   159  		if forkRight {
   160  			forkRight = false
   161  			rs = ls
   162  			this = leftChild
   163  			taskToFork = rightChild
   164  		} else {
   165  			forkRight = true
   166  			this = rightChild
   167  			taskToFork = leftChild
   168  		}
   169  
   170  		// fork
   171  		taskToFork.Fork(ctx)
   172  
   173  		sizeEstimate = rs.EstimateSize()
   174  	}
   175  	this.SetLocalResult(this.DoLeaf(ctx))
   176  }
   177  
   178  func (task *TODOShortCircuitTask) ShortCircuit(result Sink) {
   179  	if result != nil {
   180  		task.sharedResult.Store(result)
   181  	}
   182  }
   183  
   184  func (task *TODOShortCircuitTask) GetSharedResult() Sink {
   185  	return task.sharedResult.Load()
   186  }
   187  
   188  /**
   189   * Does nothing; instead, subclasses should use
   190   * {@link #setLocalResult(Object)}} to manage results.
   191   *
   192   * @param result must be null, or an exception is thrown (this is a safety
   193   *        tripwire to detect when {@code setRawResult()} is being used
   194   *        instead of {@code setLocalResult()}
   195   */
   196  func (task TODOShortCircuitTask) SetRawResult(result Sink) {
   197  	if result != nil {
   198  		panic(exception.NewIllegalStateException())
   199  	}
   200  }
   201  
   202  /**
   203   * Sets a local result for this task.  If this task is the root, set the
   204   * shared result instead (if not already set).
   205   *
   206   * @param localResult The result to set for this task
   207   */
   208  func (task *TODOShortCircuitTask) SetLocalResult(localResult Sink) {
   209  	var this = task.GetDerivedElse(task).(Task)
   210  	if this.IsRoot() {
   211  		if localResult != nil {
   212  			task.sharedResult.Store(localResult)
   213  		}
   214  	} else {
   215  		task.TODOTask.SetLocalResult(localResult)
   216  	}
   217  }
   218  
   219  /**
   220   * Retrieves the local result for this task
   221   */
   222  func (task *TODOShortCircuitTask) getRawResult() Sink {
   223  	var this = task.GetDerivedElse(task).(Task)
   224  	return this.GetLocalResult()
   225  }
   226  
   227  /**
   228   * Retrieves the local result for this task.  If this task is the root,
   229   * retrieves the shared result instead.
   230   */
   231  func (task *TODOShortCircuitTask) GetLocalResult() Sink {
   232  	var this = task.GetDerivedElse(task).(Task)
   233  	if this.IsRoot() {
   234  		answer := task.sharedResult.Load()
   235  		return answer
   236  	}
   237  	return task.TODOTask.GetLocalResult()
   238  }
   239  
   240  func (task *TODOShortCircuitTask) Cancel() {
   241  	task.canceled = true
   242  }
   243  
   244  func (task *TODOShortCircuitTask) TaskCanceled() bool {
   245  	if task.canceled {
   246  		return true
   247  	}
   248  	var this = task.GetDerivedElse(task).(Task)
   249  	parent := this.GetParent()
   250  	if parent != nil {
   251  		return parent.(ShortCircuitTask).TaskCanceled()
   252  	}
   253  	return false
   254  }
   255  
   256  func (task *TODOShortCircuitTask) CancelLaterNodes() {
   257  	// Go up the tree, cancel right siblings of this node and all parents
   258  	parent := task.GetParent()
   259  	var node = task.GetDerivedElse(task).(Task)
   260  
   261  	for parent != nil {
   262  		// If node is a left child of parent, then has a right sibling
   263  		if parent.LeftChild() == node {
   264  			rightSibling := parent.RightChild()
   265  			if !rightSibling.(*TODOShortCircuitTask).canceled {
   266  				rightSibling.(ShortCircuitTask).Cancel()
   267  			}
   268  		}
   269  
   270  		node = parent
   271  		parent = parent.GetParent()
   272  	}
   273  }