1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
// Copyright 2011 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
/*
Package runtime exposes information about the resource usage of the application.
It also provides a way to run code in a new background context of a module.
This package does not work on App Engine "flexible environment".
*/
package runtime // import "google.golang.org/appengine/runtime"
import (
"net/http"
"golang.org/x/net/context"
"google.golang.org/appengine"
"google.golang.org/appengine/internal"
pb "google.golang.org/appengine/internal/system"
)
// Statistics represents the system's statistics.
type Statistics struct {
// CPU records the CPU consumed by this instance, in megacycles.
CPU struct {
Total float64
Rate1M float64 // consumption rate over one minute
Rate10M float64 // consumption rate over ten minutes
}
// RAM records the memory used by the instance, in megabytes.
RAM struct {
Current float64
Average1M float64 // average usage over one minute
Average10M float64 // average usage over ten minutes
}
}
func Stats(c context.Context) (*Statistics, error) {
req := &pb.GetSystemStatsRequest{}
res := &pb.GetSystemStatsResponse{}
if err := internal.Call(c, "system", "GetSystemStats", req, res); err != nil {
return nil, err
}
s := &Statistics{}
if res.Cpu != nil {
s.CPU.Total = res.Cpu.GetTotal()
s.CPU.Rate1M = res.Cpu.GetRate1M()
s.CPU.Rate10M = res.Cpu.GetRate10M()
}
if res.Memory != nil {
s.RAM.Current = res.Memory.GetCurrent()
s.RAM.Average1M = res.Memory.GetAverage1M()
s.RAM.Average10M = res.Memory.GetAverage10M()
}
return s, nil
}
/*
RunInBackground makes an API call that triggers an /_ah/background request.
There are two independent code paths that need to make contact:
the RunInBackground code, and the /_ah/background handler. The matchmaker
loop arranges for the two paths to meet. The RunInBackground code passes
a send to the matchmaker, the /_ah/background passes a recv to the matchmaker,
and the matchmaker hooks them up.
*/
func init() {
http.HandleFunc("/_ah/background", handleBackground)
sc := make(chan send)
rc := make(chan recv)
sendc, recvc = sc, rc
go matchmaker(sc, rc)
}
var (
sendc chan<- send // RunInBackground sends to this
recvc chan<- recv // handleBackground sends to this
)
type send struct {
id string
f func(context.Context)
}
type recv struct {
id string
ch chan<- func(context.Context)
}
func matchmaker(sendc <-chan send, recvc <-chan recv) {
// When one side of the match arrives before the other
// it is inserted in the corresponding map.
waitSend := make(map[string]send)
waitRecv := make(map[string]recv)
for {
select {
case s := <-sendc:
if r, ok := waitRecv[s.id]; ok {
// meet!
delete(waitRecv, s.id)
r.ch <- s.f
} else {
// waiting for r
waitSend[s.id] = s
}
case r := <-recvc:
if s, ok := waitSend[r.id]; ok {
// meet!
delete(waitSend, r.id)
r.ch <- s.f
} else {
// waiting for s
waitRecv[r.id] = r
}
}
}
}
var newContext = appengine.NewContext // for testing
func handleBackground(w http.ResponseWriter, req *http.Request) {
id := req.Header.Get("X-AppEngine-BackgroundRequest")
ch := make(chan func(context.Context))
recvc <- recv{id, ch}
(<-ch)(newContext(req))
}
// RunInBackground runs f in a background goroutine in this process.
// f is provided a context that may outlast the context provided to RunInBackground.
// This is only valid to invoke from a service set to basic or manual scaling.
func RunInBackground(c context.Context, f func(c context.Context)) error {
req := &pb.StartBackgroundRequestRequest{}
res := &pb.StartBackgroundRequestResponse{}
if err := internal.Call(c, "system", "StartBackgroundRequest", req, res); err != nil {
return err
}
sendc <- send{res.GetRequestId(), f}
return nil
}
func init() {
internal.RegisterErrorCodeMap("system", pb.SystemServiceError_ErrorCode_name)
}
|