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