github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/server/appengine/camli/contextpool.go (about) 1 // +build appengine 2 3 /* 4 Copyright 2013 Google Inc. 5 6 Licensed under the Apache License, Version 2.0 (the "License"); 7 you may not use this file except in compliance with the License. 8 You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12 Unless required by applicable law or agreed to in writing, software 13 distributed under the License is distributed on an "AS IS" BASIS, 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 See the License for the specific language governing permissions and 16 limitations under the License. 17 */ 18 19 package appengine 20 21 import ( 22 "sync" 23 24 "appengine" 25 ) 26 27 type ContextPool struct { 28 mu sync.Mutex // guards live 29 30 // Live HTTP requests 31 live map[appengine.Context]*sync.WaitGroup 32 } 33 34 // HandlerBegin notes that the provided context is beginning and it can be 35 // shared until HandlerEnd is called. 36 func (p *ContextPool) HandlerBegin(c appengine.Context) { 37 p.mu.Lock() 38 defer p.mu.Unlock() 39 if p.live == nil { 40 p.live = make(map[appengine.Context]*sync.WaitGroup) 41 } 42 if _, ok := p.live[c]; ok { 43 // dup; ignore. 44 return 45 } 46 p.live[c] = new(sync.WaitGroup) 47 } 48 49 // HandlerEnd notes that the provided context is about to go out of service, 50 // removes it from the pool of available contexts, and blocks until everybody 51 // is done using it. 52 func (p *ContextPool) HandlerEnd(c appengine.Context) { 53 p.mu.Lock() 54 wg := p.live[c] 55 delete(p.live, c) 56 p.mu.Unlock() 57 if wg != nil { 58 wg.Wait() 59 } 60 } 61 62 // A ContextLoan is a superset of a Context, so can passed anywhere 63 // that needs an appengine.Context. 64 // 65 // When done, Return it. 66 type ContextLoan interface { 67 appengine.Context 68 69 // Return returns the Context to the pool. 70 // Return must be called exactly once. 71 Return() 72 } 73 74 // Get returns a valid App Engine context from some active HTTP request 75 // which is guaranteed to stay valid. Be sure to return it. 76 // 77 // Typical use: 78 // ctx := pool.Get() 79 // defer ctx.Return() 80 func (p *ContextPool) Get() ContextLoan { 81 p.mu.Lock() 82 defer p.mu.Unlock() 83 84 // Pick a random active context. TODO: pick the "right" one, 85 // using some TLS-like-guess/hack from runtume.Stacks. 86 var c appengine.Context 87 var wg *sync.WaitGroup 88 for c, wg = range p.live { 89 break 90 } 91 if c == nil { 92 panic("ContextPool.Get called with no live HTTP requests") 93 } 94 wg.Add(1) 95 cl := &contextLoan{Context: c, wg: wg} 96 // TODO: set warning finalizer on this? 97 return cl 98 } 99 100 type contextLoan struct { 101 appengine.Context 102 103 mu sync.Mutex 104 wg *sync.WaitGroup 105 } 106 107 func (cl *contextLoan) Return() { 108 cl.mu.Lock() 109 defer cl.mu.Unlock() 110 if cl.wg == nil { 111 panic("Return called twice") 112 } 113 cl.wg.Done() 114 cl.wg = nil 115 }