From cd604ef07b94ead164545763d4020fcaa716df01 Mon Sep 17 00:00:00 2001 From: Unbit Date: Thu, 9 May 2013 18:38:53 +0200 Subject: [PATCH] extended the build system to support .o files and added preliminary gccgo plugin --- plugins/gccgo/gccgo_plugin.c | 127 +++++++++++++++++++++++++++++++++++ plugins/gccgo/uwsgi.go | 107 +++++++++++++++++++++++++++++ uwsgiconfig.py | 10 +-- 3 files changed, 240 insertions(+), 4 deletions(-) create mode 100644 plugins/gccgo/gccgo_plugin.c create mode 100644 plugins/gccgo/uwsgi.go diff --git a/plugins/gccgo/gccgo_plugin.c b/plugins/gccgo/gccgo_plugin.c new file mode 100644 index 00000000..8ad1c78a --- /dev/null +++ b/plugins/gccgo/gccgo_plugin.c @@ -0,0 +1,127 @@ +#include + +/* + + Soon before official Go 1.1, we understand supporting Go in a fork() heavy + environment was not blessed by the Go community. + + Instead of completely dropping support for Go, we studied how the gccgo project works and we + decided it was a better approach for uWSGI. + + This new plugin works by initializing a new "go runtime" after each fork(). + + The runtime calls the Go main function (developed by the user), and pass the whole + uWSGI control to it. + + the uwsgi.Run() go function directly calls the uwsgi_takeover() function (it automatically + manages mules, spoolers and workers) + +*/ + +struct uwsgi_gccgo{ + struct uwsgi_string_list *libs; +} ugccgo; + +struct uwsgi_option uwsgi_gccgo_options[] = { + {"go-load", required_argument, 0, "load a go shared library in the process address space, eventually patching main.main and __go_init_main", uwsgi_opt_add_string_list, &ugccgo.libs, 0}, + {"gccgo-load", required_argument, 0, "load a go shared library in the process address space, eventually patching main.main and __go_init_main", uwsgi_opt_add_string_list, &ugccgo.libs, 0}, + //{"goroutines", required_argument, 0, "a shortcut setting optimal options for goroutine-based apps, takes the number of goroutines to spawn as argument", uwsgi_opt_setup_goroutines, NULL, UWSGI_OPT_THREADS}, + {0, 0, 0, 0, 0, 0, 0}, + +}; + +// no_split_stack is the key to avoid crashing !!! +void* runtime_m(void) __attribute__ ((noinline, no_split_stack)); + +void runtime_check(void); +void runtime_args(int, char **); +void runtime_osinit(void); +void runtime_schedinit(void); +void *__go_go(void *, void *); +void runtime_main(void); +void runtime_mstart(void *); + +extern void uwsgigo_request(void *, void *) __asm__ ("go.uwsgi.RequestHandler"); +extern void* uwsgigo_env(void *) __asm__ ("go.uwsgi.Env"); +extern void* uwsgigo_env_add(void *, void *, uint16_t, void *, uint16_t) __asm__ ("go.uwsgi.EnvAdd"); + +static void mainstart(void *arg __attribute__((unused))) { + runtime_main(); +} + +void uwsgigo_main_main(void) __asm__ ("main.main"); +void uwsgigo_main_init(void) __asm__ ("__go_init_main"); + +void (*uwsgigo_hook_init)(void); +void (*uwsgigo_hook_main)(void); + +void uwsgigo_main_init(void) { + uwsgigo_hook_init(); +} + + +void uwsgigo_main_main(void) { + uwsgigo_hook_main(); +} + +static void uwsgi_gccgo_initialize() { + struct uwsgi_string_list *usl = ugccgo.libs; + while(usl) { + void *handle = dlopen(usl->value, RTLD_NOW | RTLD_GLOBAL); + if (!handle) { + uwsgi_log("unable to open go shared library: %s\n", dlerror()); + exit(1); + } + uwsgi_log("[uwsgi-gccgo] loaded %s\n", usl->value); + uwsgigo_hook_init = dlsym(handle, "__go_init_main"); + uwsgigo_hook_main = dlsym(handle, "main.main"); + usl = usl->next; + } + if (!uwsgigo_hook_init || !uwsgigo_hook_main) { + return; + } + + // Go runtime initialization + char *argv[2] = {0}; + runtime_check(); + runtime_args(0, argv); + runtime_osinit(); + runtime_schedinit(); + __go_go(mainstart, NULL); + runtime_mstart(runtime_m()); + // never here +} + +static int uwsgi_gccgo_request(struct wsgi_request *wsgi_req) { + /* Standard GO request */ + if (!wsgi_req->uh->pktsize) { + uwsgi_log("Empty GO request. skip.\n"); + return -1; + } + + if (uwsgi_parse_vars(wsgi_req)) { + return -1; + } + + wsgi_req->async_environ = uwsgigo_env(wsgi_req); + int i; + for(i=0;ivar_cnt;i++) { + uwsgigo_env_add(wsgi_req->async_environ, wsgi_req->hvec[i].iov_base, wsgi_req->hvec[i].iov_len, wsgi_req->hvec[i+1].iov_base, wsgi_req->hvec[i+1].iov_len); + i++; + } + uwsgigo_request(wsgi_req->async_environ, wsgi_req); + return UWSGI_OK; +} + +static void uwsgi_gccgo_after_request(struct wsgi_request *wsgi_req) { + log_request(wsgi_req); +} + +struct uwsgi_plugin gccgo_plugin = { + .name = "gccgo", + .modifier1 = 11, + .options = uwsgi_gccgo_options, + .request = uwsgi_gccgo_request, + .after_request = uwsgi_gccgo_after_request, + .post_fork = uwsgi_gccgo_initialize, +}; diff --git a/plugins/gccgo/uwsgi.go b/plugins/gccgo/uwsgi.go new file mode 100644 index 00000000..5ffeacac --- /dev/null +++ b/plugins/gccgo/uwsgi.go @@ -0,0 +1,107 @@ +package uwsgi + +import ( + "strings" + "net/http" + "net/http/cgi" +) + + +//extern uwsgi_takeover +func uwsgi_takeover() +//extern uwsgi_response_write_body_do +func uwsgi_response_write_body_do(*interface{}, *byte, uint64) int +//extern uwsgi_response_prepare_headers_int +func uwsgi_response_prepare_headers_int(*interface{}, int) int +//extern uwsgi_response_add_header +func uwsgi_response_add_header(*interface{}, *byte, uint16, *byte, uint16) int +//extern uwsgi_request_body_read +func uwsgi_request_body_read(*interface{}, []byte, uint64) int + +func Env(wsgi_req *interface{}) *map[string]string { + var env map[string]string + env = make(map[string]string) + return &env +} + +func EnvAdd(env *map[string]string, k *[65536]byte, kl uint16, v *[65536]byte, vl uint16) { + (*env)[ string((*k)[0:kl]) ] = string((*v)[0:vl]) +} + +type ResponseWriter struct { + r *http.Request + wsgi_req *interface{} + headers http.Header + wroteHeader bool +} + +func (w *ResponseWriter) Write(p []byte) (n int, err error) { + if !w.wroteHeader { + w.WriteHeader(http.StatusOK) + } + uwsgi_response_write_body_do(w.wsgi_req, &p[0], uint64(len(p))) + return len(p), err +} + +func (w *ResponseWriter) WriteHeader(status int) { + uwsgi_response_prepare_headers_int(w.wsgi_req, status ) + if w.headers.Get("Content-Type") == "" { + w.headers.Set("Content-Type", "text/html; charset=utf-8") + } + for k := range w.headers { + for _, v := range w.headers[k] { + v = strings.NewReplacer("\n", " ", "\r", " ").Replace(v) + v = strings.TrimSpace(v) + kb := []byte(k) + vb := []byte(v) + uwsgi_response_add_header(w.wsgi_req, &kb[0], uint16(len(k)), &vb[0], uint16(len(v))) + } + } + w.wroteHeader = true +} + +func (w *ResponseWriter) Header() http.Header { + return w.headers +} + + +type BodyReader struct { + wsgi_req *interface{} +} + +// there is no close in request body +func (br *BodyReader) Close() error { + return nil +} + +func (br *BodyReader) Read(p []byte) (n int, err error) { + return 0, err +} +/* + m := len(p) + var body []byte = uwsgi_request_body_read(br.wsgi_req, C.ssize_t(m), &rlen) + if (body[0] == 0) { + err = io.EOF; + return 0, err + } else if (body != nil) { + return int(rlen), err + } + err = io.ErrUnexpectedEOF + rlen = 0 + return int(rlen), err +} +*/ + +func RequestHandler(env *map[string]string, wsgi_req *interface{}) { + httpReq, err := cgi.RequestFromMap(*env) + if err == nil { + httpReq.Body = &BodyReader{wsgi_req} + w := ResponseWriter{httpReq, wsgi_req,http.Header{},false} + http.DefaultServeMux.ServeHTTP(&w, httpReq) + } +} + + +func Run() { + uwsgi_takeover() +} diff --git a/uwsgiconfig.py b/uwsgiconfig.py index 3fc6e362..225811dc 100644 --- a/uwsgiconfig.py +++ b/uwsgiconfig.py @@ -1,6 +1,6 @@ # uWSGI build system -uwsgi_version = '1.9.9' +uwsgi_version = '1.9.10' import os import re @@ -153,7 +153,7 @@ def spcall3(cmd): def add_o(x): if x == 'uwsgi': x = 'main' - elif x.endswith('.a'): + elif x.endswith('.a') or x.endswith('.o'): return x x = x + '.o' return x @@ -277,7 +277,7 @@ def build_uwsgi(uc, print_only=False): objfile = file if objfile == 'uwsgi': objfile = 'main' - if not objfile.endswith('.a'): + if not objfile.endswith('.a') and not objfile.endswith('.o'): compile(' '.join(cflags), last_cflags_ts, objfile + '.o', file + '.c') if uc.get('embedded_plugins'): @@ -346,6 +346,8 @@ def build_uwsgi(uc, print_only=False): for cfile in up['GCC_LIST']: if cfile.endswith('.a'): gcc_list.append(cfile) + elif cfile.endswith('.o'): + gcc_list.append('%s/%s' % (path, cfile)) elif not cfile.endswith('.c') and not cfile.endswith('.cc') and not cfile.endswith('.m'): compile(' '.join(uniq_warnings(p_cflags)), last_cflags_ts, path + '/' + cfile + '.o', path + '/' + cfile + '.c') @@ -1122,7 +1124,7 @@ def build_plugin(path, uc, cflags, ldflags, libs, name = None): for cfile in up['GCC_LIST']: if cfile.endswith('.a'): gcc_list.append(cfile) - elif not cfile.endswith('.c') and not cfile.endswith('.cc') and not cfile.endswith('.m'): + elif not cfile.endswith('.c') and not cfile.endswith('.cc') and not cfile.endswith('.m') and not cfile.endswith('.o'): gcc_list.append(path + '/' + cfile + '.c') else: gcc_list.append(path + '/' + cfile)