datastore/transaction.go
func RunInTransaction(c context.Context, f func(tc context.Context) error, opts *TransactionOptions) error {
	xg := false
	if opts != nil {
		xg = opts.XG
	}
	readOnly := false
	if opts != nil {
		readOnly = opts.ReadOnly
	}
	attempts := 3
	if opts != nil && opts.Attempts > 0 {
		attempts = opts.Attempts
	}
	var t *pb.Transaction
	var err error
	for i := 0; i < attempts; i++ {
		if t, err = internal.RunTransactionOnce(c, f, xg, readOnly, t); err != internal.ErrConcurrentTransaction {
			return err
		}
	}
	return ErrConcurrentTransaction
}
func RunInTransaction(c context.Context, f func(tc context.Context) error, opts *TransactionOptions) error {
	xg := false
	if opts != nil {
		xg = opts.XG
	}
	readOnly := false
	if opts != nil {
		readOnly = opts.ReadOnly
	}
	attempts := 3
	if opts != nil && opts.Attempts > 0 {
		attempts = opts.Attempts
	}
	var t *pb.Transaction
	var err error
	for i := 0; i < attempts; i++ {
		if t, err = internal.RunTransactionOnce(c, f, xg, readOnly, t); err != internal.ErrConcurrentTransaction {
			return err
		}
	}
	return ErrConcurrentTransaction
}
// RunInTransaction runs f in a transaction. It calls f with a transaction
// context tc that f should use for all App Engine operations.
//
// If f returns nil, RunInTransaction attempts to commit the transaction,
// returning nil if it succeeds. If the commit fails due to a conflicting
// transaction, RunInTransaction retries f, each time with a new transaction
// context. It gives up and returns ErrConcurrentTransaction after three
// failed attempts. The number of attempts can be configured by specifying
// TransactionOptions.Attempts.
//
// If f returns non-nil, then any datastore changes will not be applied and
// RunInTransaction returns that same error. The function f is not retried.
//
// Note that when f returns, the transaction is not yet committed. Calling code
// must be careful not to assume that any of f's changes have been committed
// until RunInTransaction returns nil.
//
// Since f may be called multiple times, f should usually be idempotent.
// datastore.Get is not idempotent when unmarshaling slice fields.
//
// Nested transactions are not supported; c may not be a transaction context.