| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package memory | 5 package memory |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "errors" | 8 "errors" |
| 9 "sync" | 9 "sync" |
| 10 | 10 |
| 11 ds "github.com/luci/gae/service/datastore" | 11 ds "github.com/luci/gae/service/datastore" |
| 12 "golang.org/x/net/context" | 12 "golang.org/x/net/context" |
| 13 ) | 13 ) |
| 14 | 14 |
| 15 var serializationDeterministic = false | 15 var serializationDeterministic = false |
| 16 | 16 |
| 17 type memContextObj interface { | 17 type memContextObj interface { |
| 18 sync.Locker | 18 sync.Locker |
| 19 canApplyTxn(m memContextObj) bool | 19 canApplyTxn(m memContextObj) bool |
| 20 applyTxn(c context.Context, m memContextObj) | 20 applyTxn(c context.Context, m memContextObj) |
| 21 | 21 |
| 22 endTxn() | 22 endTxn() |
| 23 mkTxn(*ds.TransactionOptions) memContextObj | 23 mkTxn(*ds.TransactionOptions) memContextObj |
| 24 } | 24 } |
| 25 | 25 |
| 26 type memContext []memContextObj | 26 type memContext []memContextObj |
| 27 | 27 |
| 28 var _ = memContextObj((memContext)(nil)) | 28 var _ memContextObj = (*memContext)(nil) |
| 29 | 29 |
| 30 func newMemContext(aid string) memContext { | 30 func newMemContext(aid string) *memContext { |
| 31 » return memContext{ | 31 » return &memContext{ |
| 32 newTaskQueueData(), | 32 newTaskQueueData(), |
| 33 newDataStoreData(aid), | 33 newDataStoreData(aid), |
| 34 } | 34 } |
| 35 } | 35 } |
| 36 | 36 |
| 37 type memContextIdx int | 37 type memContextIdx int |
| 38 | 38 |
| 39 const ( | 39 const ( |
| 40 memContextTQIdx memContextIdx = iota | 40 memContextTQIdx memContextIdx = iota |
| 41 memContextDSIdx | 41 memContextDSIdx |
| 42 ) | 42 ) |
| 43 | 43 |
| 44 func (m memContext) Get(itm memContextIdx) memContextObj { | 44 func (m *memContext) Get(itm memContextIdx) memContextObj { |
| 45 » return m[itm] | 45 » return (*m)[itm] |
| 46 } | 46 } |
| 47 | 47 |
| 48 func (m memContext) Lock() { | 48 func (m *memContext) Lock() { |
| 49 » for _, itm := range m { | 49 » for _, itm := range *m { |
| 50 itm.Lock() | 50 itm.Lock() |
| 51 } | 51 } |
| 52 } | 52 } |
| 53 | 53 |
| 54 func (m memContext) Unlock() { | 54 func (m *memContext) Unlock() { |
| 55 » for i := len(m) - 1; i >= 0; i-- { | 55 » for i := len(*m) - 1; i >= 0; i-- { |
| 56 » » m[i].Unlock() | 56 » » (*m)[i].Unlock() |
| 57 } | 57 } |
| 58 } | 58 } |
| 59 | 59 |
| 60 func (m memContext) endTxn() { | 60 func (m *memContext) endTxn() { |
| 61 » for _, itm := range m { | 61 » for _, itm := range *m { |
| 62 itm.endTxn() | 62 itm.endTxn() |
| 63 } | 63 } |
| 64 } | 64 } |
| 65 | 65 |
| 66 func (m memContext) mkTxn(o *ds.TransactionOptions) memContextObj { | 66 func (m *memContext) mkTxn(o *ds.TransactionOptions) memContextObj { |
| 67 » ret := make(memContext, len(m)) | 67 » ret := make(memContext, len(*m)) |
| 68 » for i, itm := range m { | 68 » for i, itm := range *m { |
| 69 ret[i] = itm.mkTxn(o) | 69 ret[i] = itm.mkTxn(o) |
| 70 } | 70 } |
| 71 » return ret | 71 » return &ret |
| 72 } | 72 } |
| 73 | 73 |
| 74 func (m memContext) canApplyTxn(txnCtxObj memContextObj) bool { | 74 func (m *memContext) canApplyTxn(txnCtxObj memContextObj) bool { |
| 75 » txnCtx := txnCtxObj.(memContext) | 75 » txnCtx := *txnCtxObj.(*memContext) |
| 76 » for i := range m { | 76 » for i := range *m { |
| 77 » » if !m[i].canApplyTxn(txnCtx[i]) { | 77 » » if !(*m)[i].canApplyTxn(txnCtx[i]) { |
| 78 return false | 78 return false |
| 79 } | 79 } |
| 80 } | 80 } |
| 81 return true | 81 return true |
| 82 } | 82 } |
| 83 | 83 |
| 84 func (m memContext) applyTxn(c context.Context, txnCtxObj memContextObj) { | 84 func (m *memContext) applyTxn(c context.Context, txnCtxObj memContextObj) { |
| 85 » txnCtx := txnCtxObj.(memContext) | 85 » txnCtx := *txnCtxObj.(*memContext) |
| 86 » for i := range m { | 86 » for i := range *m { |
| 87 » » m[i].applyTxn(c, txnCtx[i]) | 87 » » (*m)[i].applyTxn(c, txnCtx[i]) |
| 88 } | 88 } |
| 89 } | 89 } |
| 90 | 90 |
| 91 // Use calls UseWithAppID with the appid of "dev~app" | 91 // Use calls UseWithAppID with the appid of "dev~app" |
| 92 func Use(c context.Context) context.Context { | 92 func Use(c context.Context) context.Context { |
| 93 return UseWithAppID(c, "dev~app") | 93 return UseWithAppID(c, "dev~app") |
| 94 } | 94 } |
| 95 | 95 |
| 96 // UseWithAppID adds implementations for the following gae interfaces to the | 96 // UseWithAppID adds implementations for the following gae interfaces to the |
| 97 // context: | 97 // context: |
| 98 // * gae.Datastore | 98 // * gae.Datastore |
| 99 // * gae.TaskQueue | 99 // * gae.TaskQueue |
| 100 // * gae.Memcache | 100 // * gae.Memcache |
| 101 // * gae.GlobalInfo | 101 // * gae.GlobalInfo |
| 102 // | 102 // |
| 103 // The application id wil be set to 'aid', and will not be modifiable in this | 103 // The application id wil be set to 'aid', and will not be modifiable in this |
| 104 // context. | 104 // context. |
| 105 // | 105 // |
| 106 // These can be retrieved with the gae.Get functions. | 106 // These can be retrieved with the gae.Get functions. |
| 107 // | 107 // |
| 108 // The implementations are all backed by an in-memory implementation, and start | 108 // The implementations are all backed by an in-memory implementation, and start |
| 109 // with an empty state. | 109 // with an empty state. |
| 110 // | 110 // |
| 111 // Using this more than once per context.Context will cause a panic. | 111 // Using this more than once per context.Context will cause a panic. |
| 112 func UseWithAppID(c context.Context, aid string) context.Context { | 112 func UseWithAppID(c context.Context, aid string) context.Context { |
| 113 if c.Value(memContextKey) != nil { | 113 if c.Value(memContextKey) != nil { |
| 114 panic(errors.New("memory.Use: called twice on the same Context")
) | 114 panic(errors.New("memory.Use: called twice on the same Context")
) |
| 115 } | 115 } |
| 116 » c = context.WithValue( | 116 » memctx := newMemContext(aid) |
| 117 » » context.WithValue(c, memContextKey, newMemContext(aid)), | 117 » c = context.WithValue(c, memContextKey, memctx) |
| 118 » » giContextKey, &globalInfoData{appid: aid}) | 118 » c = context.WithValue(c, memContextNoTxnKey, memctx) |
| 119 » c = context.WithValue(c, giContextKey, &globalInfoData{appid: aid}) |
| 120 |
| 119 return useTQ(useRDS(useMC(useGI(c, aid)))) | 121 return useTQ(useRDS(useMC(useGI(c, aid)))) |
| 120 } | 122 } |
| 121 | 123 |
| 122 func cur(c context.Context) (p memContext) { | 124 func cur(c context.Context) (p *memContext) { |
| 123 » p, _ = c.Value(memContextKey).(memContext) | 125 » p, _ = c.Value(memContextKey).(*memContext) |
| 126 » return |
| 127 } |
| 128 |
| 129 func curNoTxn(c context.Context) (p *memContext) { |
| 130 » p, _ = c.Value(memContextNoTxnKey).(*memContext) |
| 124 return | 131 return |
| 125 } | 132 } |
| 126 | 133 |
| 127 type memContextKeyType int | 134 type memContextKeyType int |
| 128 | 135 |
| 129 var memContextKey memContextKeyType | 136 var ( |
| 137 » memContextKey memContextKeyType |
| 138 » memContextNoTxnKey memContextKeyType = 1 |
| 139 ) |
| 130 | 140 |
| 131 // weird stuff | 141 // weird stuff |
| 132 | 142 |
| 133 // RunInTransaction is here because it's really a service-wide transaction, not | 143 // RunInTransaction is here because it's really a service-wide transaction, not |
| 134 // just in the datastore. TaskQueue behaves differently in a transaction in | 144 // just in the datastore. TaskQueue behaves differently in a transaction in |
| 135 // a couple ways, for example. | 145 // a couple ways, for example. |
| 136 // | 146 // |
| 137 // It really should have been appengine.Context.RunInTransaction(func(tc...)), | 147 // It really should have been appengine.Context.RunInTransaction(func(tc...)), |
| 138 // but because it's not, this method is on dsImpl instead to mirror the official | 148 // but because it's not, this method is on dsImpl instead to mirror the official |
| 139 // API. | 149 // API. |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 180 if o != nil && o.Attempts != 0 { | 190 if o != nil && o.Attempts != 0 { |
| 181 attempts = o.Attempts | 191 attempts = o.Attempts |
| 182 } | 192 } |
| 183 for attempt := 0; attempt < attempts; attempt++ { | 193 for attempt := 0; attempt < attempts; attempt++ { |
| 184 if err := loopBody(attempt >= d.data.txnFakeRetry); err != ds.Er
rConcurrentTransaction { | 194 if err := loopBody(attempt >= d.data.txnFakeRetry); err != ds.Er
rConcurrentTransaction { |
| 185 return err | 195 return err |
| 186 } | 196 } |
| 187 } | 197 } |
| 188 return ds.ErrConcurrentTransaction | 198 return ds.ErrConcurrentTransaction |
| 189 } | 199 } |
| OLD | NEW |