149 lines
3.8 KiB
Go
149 lines
3.8 KiB
Go
// 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 Managed VMs.
|
|
*/
|
|
package 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 manually scaled module.
|
|
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)
|
|
}
|