| Index: go/src/infra/gae/libs/wrapper/memory/context.go
 | 
| diff --git a/go/src/infra/gae/libs/wrapper/memory/context.go b/go/src/infra/gae/libs/wrapper/memory/context.go
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..8d295e7e46eb7500e43a86efb5c53bba753939b4
 | 
| --- /dev/null
 | 
| +++ b/go/src/infra/gae/libs/wrapper/memory/context.go
 | 
| @@ -0,0 +1,158 @@
 | 
| +// Copyright 2015 The Chromium Authors. All rights reserved.
 | 
| +// Use of this source code is governed by a BSD-style license that can be
 | 
| +// found in the LICENSE file.
 | 
| +
 | 
| +package memory
 | 
| +
 | 
| +import (
 | 
| +	"fmt"
 | 
| +	"infra/gae/libs/wrapper"
 | 
| +	"math/rand"
 | 
| +	"sync"
 | 
| +
 | 
| +	"golang.org/x/net/context"
 | 
| +
 | 
| +	"appengine/datastore"
 | 
| +)
 | 
| +
 | 
| +type memContextObj interface {
 | 
| +	sync.Locker
 | 
| +	canApplyTxn(m memContextObj) bool
 | 
| +	applyTxn(rnd *rand.Rand, m memContextObj)
 | 
| +
 | 
| +	endTxn()
 | 
| +	mkTxn(*datastore.TransactionOptions) (memContextObj, error)
 | 
| +}
 | 
| +
 | 
| +type memContext []memContextObj
 | 
| +
 | 
| +func newMemContext() memContext {
 | 
| +	return memContext{
 | 
| +		newTaskQueueData(),
 | 
| +		newDataStoreData(),
 | 
| +	}
 | 
| +}
 | 
| +
 | 
| +var memContextIndices = map[string]int{
 | 
| +	"TQ": 0,
 | 
| +	"DS": 1,
 | 
| +}
 | 
| +
 | 
| +func (m memContext) Get(item string) memContextObj {
 | 
| +	if i, ok := memContextIndices[item]; ok {
 | 
| +		return m[i]
 | 
| +	}
 | 
| +	panic(fmt.Errorf("wrapper/memory: cannot get context item %q", item))
 | 
| +}
 | 
| +
 | 
| +func (m memContext) Lock() {
 | 
| +	for _, itm := range m {
 | 
| +		itm.Lock()
 | 
| +	}
 | 
| +}
 | 
| +
 | 
| +func (m memContext) Unlock() {
 | 
| +	for i := len(m) - 1; i >= 0; i-- {
 | 
| +		m[i].Unlock()
 | 
| +	}
 | 
| +}
 | 
| +
 | 
| +func (m memContext) endTxn() {
 | 
| +	for _, itm := range m {
 | 
| +		itm.endTxn()
 | 
| +	}
 | 
| +}
 | 
| +
 | 
| +func (m memContext) mkTxn(o *datastore.TransactionOptions) (memContext, error) {
 | 
| +	ret := make(memContext, len(m))
 | 
| +	for i, itm := range m {
 | 
| +		newItm, err := itm.mkTxn(o)
 | 
| +		if err != nil {
 | 
| +			return nil, err
 | 
| +		}
 | 
| +		ret[i] = newItm
 | 
| +	}
 | 
| +	return ret, nil
 | 
| +}
 | 
| +
 | 
| +func (m memContext) canApplyTxn(txnCtx memContext) bool {
 | 
| +	for i := range m {
 | 
| +		if !m[i].canApplyTxn(txnCtx[i]) {
 | 
| +			return false
 | 
| +		}
 | 
| +	}
 | 
| +	return true
 | 
| +}
 | 
| +
 | 
| +func (m memContext) applyTxn(rnd *rand.Rand, txnCtx memContext) {
 | 
| +	for i := range m {
 | 
| +		m[i].applyTxn(rnd, txnCtx[i])
 | 
| +	}
 | 
| +}
 | 
| +
 | 
| +// Enable adds a new memory context to c. This new memory context will have
 | 
| +// a zeroed state.
 | 
| +func Enable(c context.Context) context.Context {
 | 
| +	return context.WithValue(
 | 
| +		context.WithValue(c, memContextKey, newMemContext()),
 | 
| +		giContextKey, &globalInfoData{})
 | 
| +}
 | 
| +
 | 
| +// Use calls ALL of this packages Use* methods on c. This enables all
 | 
| +// gae/wrapper Get* api's.
 | 
| +func Use(c context.Context) context.Context {
 | 
| +	return UseTQ(UseDS(UseMC(UseGI(c))))
 | 
| +}
 | 
| +
 | 
| +func cur(c context.Context) (p memContext) {
 | 
| +	p, _ = c.Value(memContextKey).(memContext)
 | 
| +	return
 | 
| +}
 | 
| +
 | 
| +type memContextKeyType int
 | 
| +
 | 
| +var memContextKey memContextKeyType
 | 
| +
 | 
| +// weird stuff
 | 
| +
 | 
| +// RunInTransaction is here because it's really a service-wide transaction, not
 | 
| +// just in the datastore. TaskQueue behaves differently in a transaction in
 | 
| +// a couple ways, for example.
 | 
| +//
 | 
| +// It really should have been appengine.Context.RunInTransaction(func(tc...)),
 | 
| +// but because it's not, this method is on dsImpl instead to mirror the official
 | 
| +// API.
 | 
| +//
 | 
| +// The fake implementation also differs from the real implementation because the
 | 
| +// fake TaskQueue is NOT backed by the fake Datastore. This is done to make the
 | 
| +// test-access API for TaskQueue better (instead of trying to reconstitute the
 | 
| +// state of the task queue from a bunch of datastore accesses).
 | 
| +func (d *dsImpl) RunInTransaction(f func(context.Context) error, o *datastore.TransactionOptions) error {
 | 
| +	curMC := cur(d.c)
 | 
| +
 | 
| +	txnMC, err := curMC.mkTxn(o)
 | 
| +	if err != nil {
 | 
| +		return err
 | 
| +	}
 | 
| +
 | 
| +	defer func() {
 | 
| +		txnMC.Lock()
 | 
| +		defer txnMC.Unlock()
 | 
| +
 | 
| +		txnMC.endTxn()
 | 
| +	}()
 | 
| +
 | 
| +	if err = f(context.WithValue(d.c, memContextKey, txnMC)); err != nil {
 | 
| +		return err
 | 
| +	}
 | 
| +
 | 
| +	txnMC.Lock()
 | 
| +	defer txnMC.Unlock()
 | 
| +
 | 
| +	if curMC.canApplyTxn(txnMC) {
 | 
| +		curMC.applyTxn(wrapper.GetMathRand(d.c), txnMC)
 | 
| +	} else {
 | 
| +		return datastore.ErrConcurrentTransaction
 | 
| +	}
 | 
| +	return nil
 | 
| +}
 | 
| 
 |