// 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 }
// 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() }
// 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) }
// 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) } }
// 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) }
// 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 }
// 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 }
// 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 }
// 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] }
// 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 }
// 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) } }
// 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) }
// Fail implements Resolver.Fail(). func (s *turnResolver) Fail(err error) { assert.True(err != nil, "Cannot fail with nil error.") s.resolve(err) }
// 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()") }
// 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) }
// 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) }
// Complete implements Resolver.Complete(). func (s *turnResolver) Complete(value interface{}) { _, isError := value.(error) assert.True(!isError, "Cannot succeed with an error.") s.resolve(value) }