コード例 #1
0
ファイル: manager.go プロジェクト: mehulsbhatt/drydock
// RunUntil runs turns in the manager until main becomes resolved.  If main fails, then its error
// is returned, otherwise returns nil.
func (m *Manager) RunUntil(main async.R) error {
	var mainExited error
	m.NewTurn("RunUntil", func() {
		async.When(main, func(err error) error {
			if err != nil {
				mainExited = err
			} else {
				mainExited = errSuccess
			}
			return err
		})
	})

	// Loop around until the main result has been resolved.  Block efficiently on I/O (so that we
	// don't spin on select) if we run out of local work to do.
	for mainExited == nil {
		// Flush the main queue.
		m.runOneLoop()

		// If there is no work to do then block on I/O.
		if m.turns.IsEmpty() && (mainExited == nil) {
			e := m.sources.Wait()
			assert.True(m.turns.IsEmpty(), "Only blocked on I/O if there was no work to do.")
			assert.True(mainExited == nil, "Only blocked on I/O if the program not exited.")
			assert.True(e != nil, "LIVE LOCK: no progress can be made because there are no I/O "+
				"sources,	no local turns, and the program has not yet exited.")
			e.Signal() // force Select in next loop to see this source again.
		}
	}

	if mainExited != errSuccess {
		return mainExited
	}
	return nil
}
コード例 #2
0
ファイル: turn.go プロジェクト: mehulsbhatt/drydock
// Run executes a single turn.
// REQUIRES: the turn be a single turn (i.e. NOT in a list).
func (list *Turn) Run() {
	assert.True(!list.IsEmpty(), "Cannot execute the empty list: %v", list)
	assert.True(list.next == nil, "Cannot execute lists, only single turns: %v.", list)

	log.V(3).Infof("%v: Run", list)

	list.f()
}
コード例 #3
0
ファイル: turn_resolver.go プロジェクト: mehulsbhatt/drydock
// Forward implements Resolver.Forward().
func (s *turnResolver) Forward(n async.ResultT) {
	assert.True(!s.isResolved(), "Cannot forward an already completed result.")

	next := async.InternalUseOnlyGetResolver(n).(*turnResolver)
	assert.True(next.manager == s.manager, "Cannot forward across managers.")

	next = next.getShortest()
	turns := s.turns
	s.turns, s.outcome, s.next = nil, nil, next
	next.queueList(turns)
}
コード例 #4
0
ファイル: turn_resolver.go プロジェクト: mehulsbhatt/drydock
// Complete implements Resolver.Resolve().
func (s *turnResolver) Resolve(value interface{}, err error) {
	_, isError := value.(error)
	assert.True(!isError, "Cannot succeed with an error.")

	if err != nil {
		assert.True(value == nil, "Provide either value or err but not both.")
		s.resolve(err)
	} else {
		s.resolve(value)
	}
}
コード例 #5
0
ファイル: turn_resolver.go プロジェクト: mehulsbhatt/drydock
// resolve completes the associated Result either successfully or as an error.
func (s *turnResolver) resolve(outcome interface{}) {
	assert.True(!s.isResolved(), "Can't resolve an already resolved result.")

	turns := s.turns
	s.turns, s.outcome = nil, outcome
	s.queueList(turns)
}
コード例 #6
0
ファイル: turn.go プロジェクト: mehulsbhatt/drydock
// Append inserts add at the end of list and returns the new resulting list.
// REQUIRES: add is NOT already in any list.
func (list *Turn) Append(add *Turn) /*newList*/ *Turn {
	assert.True(add != nil, "Can't append null to a turn queue.")
	assert.True(!add.IsEmpty(), "Can't append Empty to a turn queue.")
	assert.True(add.next == nil, "Can't append a turn that is already in a list.")

	log.V(3).Infof("%v: Append %v", list, add)
	if list.IsEmpty() {
		add.next = add // links to itself to complete the circle.
		return add
	}

	head := list.next
	add.next = head
	list.next = add
	return add
}
コード例 #7
0
ファイル: ambient.go プロジェクト: mehulsbhatt/drydock
// GetCurrentRunner returns the current ambient (default) runner.
func GetCurrentRunner() Runner {
	tid := gettid()
	threadContextLock.RLock()
	runner := threadContexts[tid].runner
	threadContextLock.RUnlock()
	assert.True(runner != nil, "GetCurrentRunner can only be called by an actor.")
	return runner
}
コード例 #8
0
ファイル: turn.go プロジェクト: mehulsbhatt/drydock
// Unlink removes a turn from a list and returns the new resulting list.
// The turn may appear anywhere in the list including the middle.
// Note: This is potentially an O(n) operation in the length of the list.
// REQUIRES: the item MUST be either a single item or in the list.
func (list *Turn) Unlink(t *Turn) /*newList*/ *Turn {
	// If the item is not in any list, then unlinking is a no-op.
	if !t.IsList() {
		log.V(3).Infof("%v: Unlink no-op %v", list, t)
		return list
	}

	assert.True(!list.IsEmpty(), "Cannot unlink from an empty list.")
	assert.True(list.IsList(), "Cannot unlink from single item.")

	log.V(3).Infof("%v: Unlink %v", list, t)

	// If the list contains only one item then it better be t.
	if list == list.next {
		assert.True(list == t, "Expected item %v to be in list %v.", t, list)

		t.next = nil // unlink the item from the list.
		return Empty
	}

	// If t is the head then just remove it.
	if list.next == t {
		list.next = t.next
		t.next = nil
		return list
	}

	// Find the item (assuming it is in the list).
	before := list.next
	for before.next != t {
		before = before.next
		assert.True(before != list, "Expected item %v to be in list %v.", t, list)
	}
	assert.True(before.next == t, "Expected head %v to be t %v", before.next, t)

	before.next = t.next
	t.next = nil

	// If we removed the list item, the before is the new list, otherwise list hasn't changed.
	if t == list {
		return before
	}
	return list
}
コード例 #9
0
ファイル: event_set.go プロジェクト: mehulsbhatt/drydock
// remove unregisters a closed event from the set.
func (w *EventSet) remove(chosen int) {
	assert.True(chosen != 0, "Never remove the default case")

	// The event has been closed.  Remove it from the set.
	// Put the last element into the relevant index and then remove the last element.
	lastIndex := len(w.events) - 1
	w.events[chosen] = w.events[lastIndex]
	w.events = w.events[:lastIndex]
	w.cases[chosen] = w.cases[lastIndex]
	w.cases = w.cases[:lastIndex]
}
コード例 #10
0
ファイル: turn.go プロジェクト: mehulsbhatt/drydock
// RemoveHead removes the item from the head of the list and returns both the item removed and the
// new resulting list.
// REQUIRES: The list MUST be non-empty.
func (list *Turn) RemoveHead() ( /*head*/ *Turn /*newList*/, *Turn) {
	assert.True(!list.IsEmpty(), "Cannot remove from empty list.")

	head := list.next

	if list.next == list {
		list = Empty
		head.next = nil
	} else {
		list.next = head.next
		head.next = nil
	}

	log.V(3).Infof("%v: RemoveHead %v", list, head)
	return head, list
}
コード例 #11
0
// dispatchTurnTests dispatches both synchronous and turn-based asynchronous tests.
func dispatchTurnTests(s *test.Suite, v reflect.Value, f reflect.Value) {
	assert.True(f.Kind() == reflect.Func, "Test function MUST be a function")

	// If it is a synchronous test method then just run it.
	if f.Type().NumOut() == 0 {
		inputs := []reflect.Value{v}
		f.Call(inputs)
		return
	}

	// If an asynchronous test method then run it within a new Actor and wait for the Actor to
	// complete.
	fn := reflect.MakeFunc(asyncTestFuncType, func(args []reflect.Value) []reflect.Value {
		// Prepend the receiver argument to the function before dispatching.
		inputs := []reflect.Value{v}
		inputs = append(inputs, args...)
		return f.Call(inputs)
	})

	err := actor.RunActor(fn.Interface().(async.Func))
	if err != nil {
		s.Errorf("Expected test case retval to succeed.  Got: %q, Want: nil", err)
	}
}
コード例 #12
0
ファイル: turn_resolver.go プロジェクト: mehulsbhatt/drydock
// When implements Resolver.WhenT().
func (s *turnResolver) WhenT(in, out, outR reflect.Type, f interface{}) async.ResultT {
	// Validate function type - this is in lieu of static type checking from generics.
	fType := reflect.TypeOf(f)
	assert.True(fType.Kind() == reflect.Func, "f MUST be a WhenFunc")

	// Validate inputs - this is in lieu of static type checking from generics.
	assert.True(fType.NumIn() <= 2, "f MUST take val, err, both or neither")
	takeValue, takeError := true, true
	if numIn := fType.NumIn(); numIn == 2 {
		assert.True(in.AssignableTo(fType.In(0)), "in MUST be assignable to value")
		assert.True(fType.In(1) == reflectTypeError, "f MUST take err")
	} else if numIn == 1 {
		takeError = (fType.In(0) == reflectTypeError)
		takeValue = !takeError
		if takeValue {
			assert.True(in.AssignableTo(fType.In(0)), "in MUST be assignable to value")
		} else {
			assert.True(fType.In(0) == reflectTypeError, "f MUST take err")
		}
	} else if numIn == 0 {
		takeValue, takeError = false, false
	}

	// Validate outputs - this is in lieu of static type checking from generics.
	assert.True(fType.NumOut() <= 2, "f MUST return val, (val, err), or async")
	returnsResult := false
	if numOut := fType.NumOut(); numOut == 2 {
		assert.True(fType.Out(0).AssignableTo(out), "value MUST be assignable to out")
		assert.True(fType.Out(1) == reflectTypeError, "err MUST be error")
	} else if numOut == 1 {
		if fType.Out(0).Implements(reflectTypeAwaitableT) {
			returnsResult = true
			assert.True(fType.Out(0) == outR, "result MUST be ResultT")
		} else if fType.Out(0) == reflectTypeError {
			assert.True(out == reflectTypeInterface, "func() error ONLY allowed on void results")
		} else {
			assert.True(fType.Out(0).AssignableTo(out), "value MUST be assignable to out")
		}
	} else {
		assert.True(out == reflectTypeInterface, "func() ONLY allowed on void results")
	}

	// If this result is already forwarded then just forward the When as well.
	if s.next != nil {
		return s.getShortest().WhenT(in, out, outR, f)
	}

	// Create a new turn that will run once the result is resolved.
	outer := newTurnResolver(s.manager)
	turn := NewTurn("When"+s.manager.NewID().String(), func() {
		// Find the resolved value.
		final := s.getShortest()
		assert.True(final.isResolved(), "When's shouldn't run if the target is not resolved.")

		// Distinguish the error from the value.
		value := final.outcome
		err, isError := final.outcome.(error)
		if isError {
			value = nil
		}

		// If f doesn't take an error but the previous computation failed, then just flow it through to
		// the output by failing immediately.  This allows for null-style error propagation which
		// simplifies handlers since that is a very common case.  f is NEVER called in this case under
		// the assumption that its first line would be: if err != nil { return err }.
		if !takeError {
			if err != nil {
				outer.Fail(err)
				return
			}
		}

		// Convert the arguments into an array of Values
		args := make([]reflect.Value, 0, 2)
		if takeValue {
			if value == nil {
				args = append(args, reflect.New(in).Elem())
			} else {
				args = append(args, reflect.ValueOf(value))
			}
		}
		if takeError {
			if err == nil {
				args = append(args, reflect.New(reflectTypeError).Elem())
			} else {
				args = append(args, reflect.ValueOf(err))
			}
		}

		// Dispatch the result to the WhenFunc.
		retvals := reflect.ValueOf(f).Call(args)

		// Resolve the outer result.
		if returnsResult {
			outer.Forward(retvals[0].Interface().(async.AwaitableT).Base())
			return
		}
		if numRets := len(retvals); numRets == 2 {
			if err, _ = retvals[1].Interface().(error); err != nil {
				outer.resolve(err)
			} else {
				outer.resolve(retvals[0].Interface())
			}
		} else if numRets == 1 {
			outer.resolve(retvals[0].Interface())
		} else {
			outer.resolve(nil)
		}
	})
	s.queue(turn)
	return async.NewResultT(outer)
}
コード例 #13
0
ファイル: turn_resolver.go プロジェクト: mehulsbhatt/drydock
// Fail implements Resolver.Fail().
func (s *turnResolver) Fail(err error) {
	assert.True(err != nil, "Cannot fail with nil error.")
	s.resolve(err)
}
コード例 #14
0
ファイル: assert_test.go プロジェクト: mehulsbhatt/drydock
// Coverage provides minimal code coverage on the non-failing assert paths.
func (t *AssertSuite) Coverage() {
	assert.True(true, "Code coverage for assert.True()")
	assert.False(false, "Code coverage for assert.False()")
}
コード例 #15
0
ファイル: test_suite.go プロジェクト: mehulsbhatt/drydock
// getFileLine returns the caller's file and line number in the format file:line.
func (t *Suite) getFileLine() string {
	_, file, line, ok := runtime.Caller(2)
	assert.True(ok, "The caller MUST always be available for user code")

	return fmt.Sprintf("%s:%d", file, line)
}
コード例 #16
0
ファイル: test_suite.go プロジェクト: mehulsbhatt/drydock
// dispatchSyncTests dispatches a synchronous test method that takes no arguments and has no return
// value.  Dispatch waits for the test method to complete.
func dispatchSyncTests(s *Suite, v reflect.Value, f reflect.Value) {
	assert.True(f.Kind() == reflect.Func, "Test function MUST be a function")

	inputs := []reflect.Value{v}
	f.Call(inputs)
}
コード例 #17
0
ファイル: turn_resolver.go プロジェクト: mehulsbhatt/drydock
// Complete implements Resolver.Complete().
func (s *turnResolver) Complete(value interface{}) {
	_, isError := value.(error)
	assert.True(!isError, "Cannot succeed with an error.")

	s.resolve(value)
}