OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 package memory | |
6 | |
7 import ( | |
8 "errors" | |
9 "sync" | |
10 | |
11 "github.com/luci/gae" | |
12 "golang.org/x/net/context" | |
13 ) | |
14 | |
15 type memContextObj interface { | |
16 sync.Locker | |
17 canApplyTxn(m memContextObj) bool | |
18 applyTxn(c context.Context, m memContextObj) | |
19 | |
20 endTxn() | |
21 mkTxn(*gae.DSTransactionOptions) memContextObj | |
22 } | |
23 | |
24 type memContext []memContextObj | |
25 | |
26 var _ = memContextObj((memContext)(nil)) | |
27 | |
28 func newMemContext() memContext { | |
29 return memContext{ | |
30 newTaskQueueData(), | |
31 newDataStoreData(), | |
32 } | |
33 } | |
34 | |
35 type memContextIdx int | |
36 | |
37 const ( | |
38 memContextTQIdx memContextIdx = iota | |
39 memContextDSIdx | |
40 ) | |
41 | |
42 func (m memContext) Get(itm memContextIdx) memContextObj { | |
43 return m[itm] | |
44 } | |
45 | |
46 func (m memContext) Lock() { | |
47 for _, itm := range m { | |
48 itm.Lock() | |
49 } | |
50 } | |
51 | |
52 func (m memContext) Unlock() { | |
53 for i := len(m) - 1; i >= 0; i-- { | |
54 m[i].Unlock() | |
55 } | |
56 } | |
57 | |
58 func (m memContext) endTxn() { | |
59 for _, itm := range m { | |
60 itm.endTxn() | |
61 } | |
62 } | |
63 | |
64 func (m memContext) mkTxn(o *gae.DSTransactionOptions) memContextObj { | |
65 ret := make(memContext, len(m)) | |
66 for i, itm := range m { | |
67 ret[i] = itm.mkTxn(o) | |
68 } | |
69 return ret | |
70 } | |
71 | |
72 func (m memContext) canApplyTxn(txnCtxObj memContextObj) bool { | |
73 txnCtx := txnCtxObj.(memContext) | |
74 for i := range m { | |
75 if !m[i].canApplyTxn(txnCtx[i]) { | |
76 return false | |
77 } | |
78 } | |
79 return true | |
80 } | |
81 | |
82 func (m memContext) applyTxn(c context.Context, txnCtxObj memContextObj) { | |
83 txnCtx := txnCtxObj.(memContext) | |
84 for i := range m { | |
85 m[i].applyTxn(c, txnCtx[i]) | |
86 } | |
87 } | |
88 | |
89 // Use adds implementations for the following gae interfaces to the | |
90 // context: | |
91 // * gae.Datastore | |
92 // * gae.TaskQueue | |
93 // * gae.Memcache | |
94 // * gae.GlobalInfo | |
95 // | |
96 // These can be retrieved with the gae.Get functions. | |
97 // | |
98 // The implementations are all backed by an in-memory implementation, and start | |
99 // with an empty state. | |
100 // | |
101 // Using this more than once per context.Context will cause a panic. | |
102 func Use(c context.Context) context.Context { | |
103 if c.Value(memContextKey) != nil { | |
104 panic(errors.New("memory.Use: called twice on the same Context")
) | |
105 } | |
106 c = context.WithValue( | |
107 context.WithValue(c, memContextKey, newMemContext()), | |
108 giContextKey, &globalInfoData{}) | |
109 return useTQ(useRDS(useMC(useGI(c)))) | |
110 } | |
111 | |
112 func cur(c context.Context) (p memContext) { | |
113 p, _ = c.Value(memContextKey).(memContext) | |
114 return | |
115 } | |
116 | |
117 type memContextKeyType int | |
118 | |
119 var memContextKey memContextKeyType | |
120 | |
121 // weird stuff | |
122 | |
123 // RunInTransaction is here because it's really a service-wide transaction, not | |
124 // just in the datastore. TaskQueue behaves differently in a transaction in | |
125 // a couple ways, for example. | |
126 // | |
127 // It really should have been appengine.Context.RunInTransaction(func(tc...)), | |
128 // but because it's not, this method is on dsImpl instead to mirror the official | |
129 // API. | |
130 // | |
131 // The fake implementation also differs from the real implementation because the | |
132 // fake TaskQueue is NOT backed by the fake Datastore. This is done to make the | |
133 // test-access API for TaskQueue better (instead of trying to reconstitute the | |
134 // state of the task queue from a bunch of datastore accesses). | |
135 func (d *dsImpl) RunInTransaction(f func(context.Context) error, o *gae.DSTransa
ctionOptions) error { | |
136 curMC := cur(d.c) | |
137 | |
138 txnMC := curMC.mkTxn(o) | |
139 | |
140 defer func() { | |
141 txnMC.Lock() | |
142 defer txnMC.Unlock() | |
143 | |
144 txnMC.endTxn() | |
145 }() | |
146 | |
147 if err := f(context.WithValue(d.c, memContextKey, txnMC)); err != nil { | |
148 return err | |
149 } | |
150 | |
151 txnMC.Lock() | |
152 defer txnMC.Unlock() | |
153 | |
154 if curMC.canApplyTxn(txnMC) { | |
155 curMC.applyTxn(d.c, txnMC) | |
156 } else { | |
157 return gae.ErrDSConcurrentTransaction | |
158 } | |
159 return nil | |
160 } | |
OLD | NEW |