added experimental fiber support for ruby1.9

This commit is contained in:
roberto@sirius
2010-11-17 07:42:51 +01:00
parent 0bd017f722
commit a4120efabe
9 changed files with 175 additions and 137 deletions
+3 -2
View File
@@ -1,13 +1,14 @@
#require 'fiber'
require 'fiber'
require 'sinatra'
get '/hi' do
for i in 1..10
puts "ruby"
#UWSGI.suspend()
#Fiber.yield
end
"Hello World!"
end
+2 -2
View File
@@ -83,7 +83,7 @@ void log_request(struct wsgi_request *wsgi_req) {
}
rlen = snprintf(logpkt, 4096, "[pid: %d|app: %d|req: %d/%llu] %.*s (%.*s) {%d vars in %d bytes} [%.*s] %.*s %.*s => generated %llu bytes in %ld msecs%s(%.*s %d) %d headers in %d bytes (%d switches on core %d)\n",
rlen = snprintf(logpkt, 4096, "[pid: %d|app: %d|req: %d/%llu] %.*s (%.*s) {%d vars in %d bytes} [%.*s] %.*s %.*s => generated %llu bytes in %ld msecs%s(%.*s %d) %d headers in %llu bytes (%d switches on core %d)\n",
(int) uwsgi.mypid,
wsgi_req->app_id,
app_req,
@@ -101,7 +101,7 @@ void log_request(struct wsgi_request *wsgi_req) {
wsgi_req->protocol_len, wsgi_req->protocol,
wsgi_req->status,
wsgi_req->header_cnt,
wsgi_req->headers_size,
(unsigned long long) wsgi_req->headers_size,
wsgi_req->switches, wsgi_req->async_id);
logvec[logvecpos].iov_base = logpkt;
+53 -30
View File
@@ -1,15 +1,20 @@
#include "../../uwsgi.h"
#include "uwsgi_rack.h"
extern struct uwsgi_server uwsgi;
#include <ruby.h>
extern struct uwsgi_rack ur;
VALUE fiber_list[200];
void uwsgi_ruby_exception(void);
VALUE fiber_request(VALUE core_id) {
uwsgi_log("i am the fiber\n");
int async_id = NUM2INT(core_id);
uwsgi_log("i am the fiber %d\n", async_id);
struct wsgi_request *wsgi_req = uwsgi.wsgi_requests[async_id];
@@ -26,20 +31,30 @@ VALUE fiber_request(VALUE core_id) {
if (wsgi_req_accept(wsgi_req)) {
continue;
}
uwsgi_log("request on fiber %d accepted\n", async_id);
wsgi_req->async_status = UWSGI_OK;
// reinitialize switches counter
wsgi_req->switches = 0;
if (wsgi_req_recv(wsgi_req)) {
continue;
}
uwsgi_log("FIBER %d HAS DONE\n", async_id);
while(wsgi_req->async_status == UWSGI_AGAIN) {
uwsgi_log("ASYNC APP DETECTED\n");
rb_fiber_yield(0, NULL);
wsgi_req->async_status = uwsgi.shared->hook_request[wsgi_req->uh.modifier1](wsgi_req);
wsgi_req->async_status = uwsgi.p[wsgi_req->uh.modifier1]->request(wsgi_req);
}
uwsgi_log("A LAST YIELD FOR %d\n", async_id);
rb_fiber_yield(0, NULL);
uwsgi_log("CLOSING REQUEST\n");
uwsgi_close_request(wsgi_req);
}
@@ -48,42 +63,50 @@ VALUE fiber_request(VALUE core_id) {
}
VALUE fiber_create(VALUE core_id) {
VALUE protected_fiber_loop() {
return rb_fiber_new( fiber_request, core_id );
}
int i, current = 0;
VALUE core_id;
VALUE fiber_resume(VALUE core_id) {
// create a ruby fiber for each async core
uwsgi_log("create a fiber for each async core...\n");
for(i=0;i<uwsgi.async;i++) {
uwsgi_log("creating fiber %d\n", i);
fiber_list[i] = rb_fiber_new(fiber_request, INT2NUM(i));
uwsgi_log("fiber %d ready\n", i);
}
for(;;) {
uwsgi_log("resuming fiber %d %p\n", current, fiber_list[current]);
core_id = INT2NUM(current);
uwsgi_log("go resume %p!!\n", core_id);
uwsgi.wsgi_req = uwsgi.wsgi_requests[current];
uwsgi.wsgi_req->switches++;
rb_fiber_resume(fiber_list[current], 1, &core_id);
current++;
if (current >= uwsgi.async) current = 0;
}
rb_fiber_resume( fiber_list[NUM2INT(core_id)], 0, NULL );
uwsgi_log("fiber yielded\n");
return Qnil;
}
void fiber_loop() {
int error;
int i;
int current = 0;
// create a ruby fiber for each async core
uwsgi_log("create a fiber for each async core...\n");
for(i=0;i<uwsgi.async;i++) {
uwsgi_log("creating fiber %d\n", i);
fiber_list[i] = rb_protect(fiber_create, INT2NUM(i), 0);
uwsgi_log("fiber %d ready\n", i);
// must run all the rack/ruby plugins without protection
ur.unprotected = 1;
rb_protect(protected_fiber_loop, 0, &error);
if (error) {
uwsgi_ruby_exception();
exit(1);
}
// wait for io or resume if there are fiber in no-accepting state
for(;;) {
uwsgi_log("resuming fiber %d %p\n", current, fiber_list[current]);
rb_funcall(fiber_list[current], rb_intern("resume"), 0);
current++;
if (current >= uwsgi.async) current = 0;
}
// never here
}
+48 -90
View File
@@ -1,66 +1,8 @@
#include "../../uwsgi.h"
#include <ruby.h>
#include "uwsgi_rack.h"
extern struct uwsgi_server uwsgi;
#define LONG_ARGS_RACK_BASE 17000 + (7 * 100)
#define LONG_ARGS_RAILS LONG_ARGS_RACK_BASE + 1
#define LONG_ARGS_RUBY_GC_FREQ LONG_ARGS_RACK_BASE + 2
#define LONG_ARGS_RACK LONG_ARGS_RACK_BASE + 3
#ifndef RUBY19
#define rb_errinfo() ruby_errinfo
#define RUBY_GVL_LOCK
#define RUBY_GVL_UNLOCK
#else
//void fiber_loop(void);
#ifdef UWSGI_THREADING
#define RUBY_GVL_LOCK if (uwsgi.threads > 1) {\
pthread_mutex_lock(&ur.gvl);\
}
#define RUBY_GVL_UNLOCK if (uwsgi.threads > 1) {\
pthread_mutex_unlock(&ur.gvl);\
}
#else
#define RUBY_GVL_LOCK
#define RUBY_GVL_UNLOCK
#endif
#endif
#ifndef RARRAY_LEN
#define RARRAY_LEN(x) RARRAY(x)->len
#endif
#ifndef RARRAY_PTR
#define RARRAY_PTR(x) RARRAY(x)->ptr
#endif
#ifndef RSTRING_PTR
#define RSTRING_PTR(x) RSTRING(x)->ptr
#endif
#ifndef RSTRING_LEN
#define RSTRING_LEN(x) RSTRING(x)->len
#endif
struct uwsgi_rack {
char *rails;
char *rack;
int gc_freq;
uint64_t cycles;
int call_gc;
VALUE dispatcher;
VALUE rb_uwsgi_io_class;
ID call;
VALUE fibers[200];
pthread_mutex_t gvl;
} ur;
struct uwsgi_rack ur;
struct option uwsgi_rack_options[] = {
@@ -72,7 +14,7 @@ struct option uwsgi_rack_options[] = {
};
static void uwsgi_ruby_exception(void) {
void uwsgi_ruby_exception(void) {
VALUE lasterr = rb_gv_get("$!");
VALUE message = rb_obj_as_string(lasterr);
@@ -313,11 +255,7 @@ RUBY_GLOBAL_SETUP
#endif
VALUE require_rack(VALUE arg) {
#ifdef RUBY19
return rb_require("rack");
#else
return rb_funcall(rb_cObject, rb_intern("require"), 1, rb_str_new2("rack"));
#endif
}
VALUE require_rails(VALUE arg) {
@@ -330,6 +268,12 @@ VALUE require_rails(VALUE arg) {
VALUE init_rack_app(VALUE);
#ifdef RUBY19
VALUE uwsgi_ruby_fiber_yield() {
return rb_fiber_yield(0, NULL);
}
#endif
VALUE uwsgi_ruby_suspend(VALUE *arg) {
struct wsgi_request *wsgi_req = current_wsgi_req();
@@ -365,16 +309,20 @@ int uwsgi_rack_init(){
#else
ruby_init();
ruby_script("uwsgi");
ruby_init_loadpath();
#endif
ruby_script("uwsgi");
#ifdef RUBY19
//uwsgi_register_loop( (char *) "fiber", fiber_loop);
uwsgi_register_loop( (char *) "fiber", fiber_loop);
#endif
VALUE rb_uwsgi_embedded = rb_define_module("UWSGI");
rb_define_module_function(rb_uwsgi_embedded, "suspend", uwsgi_ruby_suspend, 0);
#ifdef RUBY19
rb_define_module_function(rb_uwsgi_embedded, "fiber_yield", uwsgi_ruby_fiber_yield, 0);
#endif
if (ur.rack) {
@@ -448,7 +396,7 @@ VALUE call_dispatch(VALUE env) {
VALUE send_body(VALUE obj) {
struct wsgi_request *wsgi_req = current_wsgi_req();
size_t len;
ssize_t len = 0;
int fd = wsgi_req->poll.fd;
//uwsgi_log("sending body\n");
@@ -459,22 +407,13 @@ VALUE send_body(VALUE obj) {
uwsgi_log("UNMANAGED BODY TYPE %d\n", TYPE(obj));
}
wsgi_req->response_size += len;
uwsgi_log("body sent forcore %d\n", wsgi_req->async_id);
return Qnil;
}
VALUE safe_each(VALUE arg) {
int error = 0;
VALUE result;
result = rb_protect(rb_each, arg, &error);
if (error) {
uwsgi_ruby_exception();
return Qnil;
}
return result;
}
VALUE iterate_body(VALUE body) {
#ifdef RUBY19
@@ -562,7 +501,7 @@ int uwsgi_rack_request(struct wsgi_request *wsgi_req) {
}
RUBY_GVL_LOCK
//RUBY_GVL_LOCK
env = rb_hash_new();
@@ -602,13 +541,19 @@ int uwsgi_rack_request(struct wsgi_request *wsgi_req) {
rb_hash_aset(env, rb_str_new2("rack.errors"), rb_funcall( rb_const_get(rb_cObject, rb_intern("IO")), rb_intern("new"), 2, INT2NUM(2), rb_str_new("w",1) ));
ret = rb_protect( call_dispatch, env, &error);
if (ur.unprotected) {
ret = rb_funcall(ur.dispatcher, ur.call, 1, env);
}
else {
ret = rb_protect( call_dispatch, env, &error);
}
if (error) {
uwsgi_ruby_exception();
//return -1;
}
uwsgi_log("ready to manage response\n");
if (TYPE(ret) == T_ARRAY) {
if (RARRAY_LEN(ret) != 3) {
uwsgi_log("Invalid RACK response size: %d\n", RARRAY_LEN(ret));
@@ -648,7 +593,7 @@ int uwsgi_rack_request(struct wsgi_request *wsgi_req) {
wsgi_req->hvec[5].iov_len = 2 ;
//RUBY_GVL_UNLOCK
if ( !(wsgi_req->response_size = writev(wsgi_req->poll.fd, wsgi_req->hvec, 6)) ) {
if ( !(wsgi_req->headers_size = writev(wsgi_req->poll.fd, wsgi_req->hvec, 6)) ) {
uwsgi_error("writev()");
}
//RUBY_GVL_LOCK
@@ -681,18 +626,31 @@ int uwsgi_rack_request(struct wsgi_request *wsgi_req) {
}
else if (rb_respond_to( body, rb_intern("each") )) {
rb_protect( iterate_body, body, &error);
if (error) {
uwsgi_ruby_exception();
if (ur.unprotected) {
#ifdef RUBY19
rb_block_call(body, rb_intern("each"), 0, 0, send_body, 0);
uwsgi_log("CORE %d HAS FINISHED LOOPING BODY\n", wsgi_req->async_id);
#else
rb_iterate(rb_each, body, send_body, 0);
#endif
}
else {
rb_protect( iterate_body, body, &error);
if (error) {
uwsgi_ruby_exception();
}
}
}
if (rb_respond_to( body, rb_intern("close") )) {
//uwsgi_log("calling close\n");
uwsgi_log("CALLING CLOSE ON CORE %d\n", wsgi_req->async_id);
rb_funcall( body, rb_intern("close"), 0);
}
//fine:
uwsgi_log("UNREGISTERING OBJECTS ON CORE %d\n", wsgi_req->async_id);
/* unregister all the objects created */
rb_gc_unregister_address(&status);
rb_gc_unregister_address(&headers);
@@ -727,7 +685,7 @@ int uwsgi_rack_request(struct wsgi_request *wsgi_req) {
#endif
}
RUBY_GVL_UNLOCK
//RUBY_GVL_UNLOCK
ur.cycles++;
@@ -794,12 +752,12 @@ VALUE init_rack_app( VALUE script ) {
VALUE rack = rb_const_get(rb_cObject, rb_intern("Rack"));
VALUE rackup = rb_funcall( rb_const_get(rack, rb_intern("Builder")), rb_intern("parse_file"), 1, script);
if (TYPE(rackup) != T_ARRAY) {
uwsgi_log("unable to parse %s file\n", RSTRING(script)->ptr);
uwsgi_log("unable to parse %s file\n", RSTRING_PTR(script));
return Qnil;
}
if (RARRAY_LEN(rackup) < 1) {
uwsgi_log("invalid rack config file: %s\n", RSTRING(script)->ptr);
uwsgi_log("invalid rack config file: %s\n", RSTRING_PTR(script));
return Qnil;
}
+62
View File
@@ -0,0 +1,62 @@
#include "../../uwsgi.h"
#include <ruby.h>
#define LONG_ARGS_RACK_BASE 17000 + (7 * 100)
#define LONG_ARGS_RAILS LONG_ARGS_RACK_BASE + 1
#define LONG_ARGS_RUBY_GC_FREQ LONG_ARGS_RACK_BASE + 2
#define LONG_ARGS_RACK LONG_ARGS_RACK_BASE + 3
#ifndef RUBY19
#define rb_errinfo() ruby_errinfo
#define RUBY_GVL_LOCK
#define RUBY_GVL_UNLOCK
#else
void fiber_loop(void);
#ifdef UWSGI_THREADING
#define RUBY_GVL_LOCK if (uwsgi.threads > 1) {\
pthread_mutex_lock(&ur.gvl);\
}
#define RUBY_GVL_UNLOCK if (uwsgi.threads > 1) {\
pthread_mutex_unlock(&ur.gvl);\
}
#else
#define RUBY_GVL_LOCK
#define RUBY_GVL_UNLOCK
#endif
#endif
#ifndef RARRAY_LEN
#define RARRAY_LEN(x) RARRAY(x)->len
#endif
#ifndef RARRAY_PTR
#define RARRAY_PTR(x) RARRAY(x)->ptr
#endif
#ifndef RSTRING_PTR
#define RSTRING_PTR(x) RSTRING(x)->ptr
#endif
#ifndef RSTRING_LEN
#define RSTRING_LEN(x) RSTRING(x)->len
#endif
struct uwsgi_rack {
char *rails;
char *rack;
int gc_freq;
uint64_t cycles;
int call_gc;
VALUE dispatcher;
VALUE rb_uwsgi_io_class;
ID call;
VALUE fibers[200];
int unprotected;
pthread_mutex_t gvl;
};
+3 -1
View File
@@ -12,9 +12,12 @@ CFLAGS = os.popen(RUBYPATH + " -e \"require 'rbconfig';print Config::CONFIG['CFL
version = os.popen(RUBYPATH + " -e \"print RUBY_VERSION\"").read().rstrip()
v = version.split('.')
GCC_LIST = ['rack_plugin']
if v[0] == '1' and v[1] == '9':
CFLAGS.append('-DRUBY19')
CFLAGS.append('-Wno-unused-parameter')
GCC_LIST.append('fiber')
includedir = os.popen(RUBYPATH + " -e \"require 'rbconfig';print Config::CONFIG['rubyhdrdir']\"").read().rstrip()
if includedir == 'nil':
@@ -34,5 +37,4 @@ libpath = os.popen(RUBYPATH + " -e \"require 'rbconfig';print Config::CONFIG['li
LDFLAGS.append('-L' + libpath )
os.environ['LD_RUN_PATH'] = libpath
LIBS = os.popen(RUBYPATH + " -e \"require 'rbconfig';print '-l' + Config::CONFIG['RUBY_SO_NAME']\"").read().rstrip().split()
GCC_LIST = ['rack_plugin']
+3 -1
View File
@@ -1,8 +1,10 @@
require 'fiber'
class SuspendingBody
def each
for i in 1..100
for i in 1..10
yield "numero: #{i}\n"
Fiber.yield
end
end
+1 -1
View File
@@ -513,7 +513,7 @@ struct wsgi_request {
int status;
size_t response_size;
int headers_size;
ssize_t headers_size;
int async_id;
int async_status;
-10
View File
@@ -171,16 +171,6 @@ class uConf():
def get_gcll(self):
kvm_list = ['FreeBSD', 'OpenBSD', 'NetBSD', 'DragonFly']
if uwsgi_os == 'Darwin':
# build a universal binary on osx
self.cflags.append('-arch i386')
self.cflags.append('-arch x86_64')
self.cflags.append('-arch ppc')
self.ldflags.append('-arch i386')
self.ldflags.append('-arch x86_64')
self.ldflags.append('-arch ppc')
if uwsgi_os == 'SunOS':
self.libs.append('-lsendfile')
if not uwsgi_os_v.startswith('Nexenta'):