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  }