aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/google.golang.org/appengine/runtime/runtime.go
blob: fa6c12b79ca1cc182a5dcc54ac977e241804a275 (plain)
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)
}