// Perform the actual HTTP request by calling libcurl. static VALUE perform_request(VALUE self) { struct curl_state *state; Data_Get_Struct(self, struct curl_state, state); CURL* curl = state->handle; // headers VALUE header_buffer = rb_str_buf_new(32768); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, &session_write_handler); curl_easy_setopt(curl, CURLOPT_HEADERDATA, header_buffer); // body VALUE body_buffer = Qnil; if (!state->download_file) { body_buffer = rb_str_buf_new(32768); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &session_write_handler); curl_easy_setopt(curl, CURLOPT_WRITEDATA, body_buffer); } #if defined(HAVE_TBR) && defined(USE_TBR) CURLcode ret = rb_thread_blocking_region(curl_easy_perform, curl, RUBY_UBF_IO, 0); #else CURLcode ret = curl_easy_perform(curl); #endif if (CURLE_OK == ret) { VALUE response = create_response(curl); if (!NIL_P(body_buffer)) { rb_iv_set(response, "@body", body_buffer); } rb_funcall(response, rb_intern("parse_headers"), 1, header_buffer); return response; } else { rb_raise(select_error(ret), "%s", state->error_buf); } }
static VALUE rb_mysql_client_store_result(VALUE self) { MYSQL_RES * result; VALUE resultObj; #ifdef HAVE_RUBY_ENCODING_H mysql2_result_wrapper * result_wrapper; #endif GET_CLIENT(self); // MYSQL_RES* res = mysql_store_result(wrapper->client); // if (res == NULL) // mysql_raise(wrapper->client); // return mysqlres2obj(res); result = (MYSQL_RES *)rb_thread_blocking_region(nogvl_store_result, wrapper, RUBY_UBF_IO, 0); if (result == NULL) { if (mysql_errno(wrapper->client) != 0) { rb_raise_mysql2_error(wrapper); } // no data and no error, so query was not a SELECT return Qnil; } resultObj = rb_mysql_result_to_obj(result); // pass-through query options for result construction later rb_iv_set(resultObj, "@query_options", rb_funcall(rb_iv_get(self, "@query_options"), rb_intern("dup"), 0)); #ifdef HAVE_RUBY_ENCODING_H GetMysql2Result(resultObj, result_wrapper); result_wrapper->encoding = wrapper->encoding; #endif return resultObj; }
static VALUE rb_mysql_result_fetch_row(int argc, VALUE * argv, VALUE self) { VALUE rowHash, opts, block; mysql2_result_wrapper * wrapper; MYSQL_ROW row; MYSQL_FIELD * fields = NULL; unsigned int i = 0, symbolizeKeys = 0; unsigned long * fieldLengths; void * ptr; #ifdef HAVE_RUBY_ENCODING_H rb_encoding *default_internal_enc = rb_default_internal_encoding(); rb_encoding *conn_enc = rb_to_encoding(rb_iv_get(self, "@encoding")); #endif GetMysql2Result(self, wrapper); if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1) { Check_Type(opts, T_HASH); if (rb_hash_aref(opts, sym_symbolize_keys) == Qtrue) { symbolizeKeys = 1; } } ptr = wrapper->result; row = (MYSQL_ROW)rb_thread_blocking_region(nogvl_fetch_row, ptr, RUBY_UBF_IO, 0); if (row == NULL) { return Qnil; } rowHash = rb_hash_new(); fields = mysql_fetch_fields(wrapper->result); fieldLengths = mysql_fetch_lengths(wrapper->result); if (wrapper->fields == Qnil) { wrapper->numberOfFields = mysql_num_fields(wrapper->result); wrapper->fields = rb_ary_new2(wrapper->numberOfFields); } for (i = 0; i < wrapper->numberOfFields; i++) { VALUE field = rb_mysql_result_fetch_field(self, i, symbolizeKeys); if (row[i]) { VALUE val; switch(fields[i].type) { case MYSQL_TYPE_NULL: // NULL-type field val = Qnil; break; case MYSQL_TYPE_BIT: // BIT field (MySQL 5.0.3 and up) val = rb_str_new(row[i], fieldLengths[i]); break; case MYSQL_TYPE_TINY: // TINYINT field case MYSQL_TYPE_SHORT: // SMALLINT field case MYSQL_TYPE_LONG: // INTEGER field case MYSQL_TYPE_INT24: // MEDIUMINT field case MYSQL_TYPE_LONGLONG: // BIGINT field case MYSQL_TYPE_YEAR: // YEAR field val = rb_cstr2inum(row[i], 10); break; case MYSQL_TYPE_DECIMAL: // DECIMAL or NUMERIC field case MYSQL_TYPE_NEWDECIMAL: // Precision math DECIMAL or NUMERIC field (MySQL 5.0.3 and up) val = rb_funcall(cBigDecimal, intern_new, 1, rb_str_new(row[i], fieldLengths[i])); break; case MYSQL_TYPE_FLOAT: // FLOAT field case MYSQL_TYPE_DOUBLE: // DOUBLE or REAL field val = rb_float_new(strtod(row[i], NULL)); break; case MYSQL_TYPE_TIME: { // TIME field int hour, min, sec, tokens; tokens = sscanf(row[i], "%2d:%2d:%2d", &hour, &min, &sec); val = rb_funcall(rb_cTime, intern_utc, 6, INT2NUM(0), INT2NUM(1), INT2NUM(1), INT2NUM(hour), INT2NUM(min), INT2NUM(sec)); break; } case MYSQL_TYPE_TIMESTAMP: // TIMESTAMP field case MYSQL_TYPE_DATETIME: { // DATETIME field int year, month, day, hour, min, sec, tokens; tokens = sscanf(row[i], "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &min, &sec); if (year+month+day+hour+min+sec == 0) { val = Qnil; } else { if (month < 1 || day < 1) { rb_raise(cMysql2Error, "Invalid date: %s", row[i]); val = Qnil; } else { val = rb_funcall(rb_cTime, intern_utc, 6, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec)); } } break; } case MYSQL_TYPE_DATE: // DATE field case MYSQL_TYPE_NEWDATE: { // Newer const used > 5.0 int year, month, day, tokens; tokens = sscanf(row[i], "%4d-%2d-%2d", &year, &month, &day); if (year+month+day == 0) { val = Qnil; } else { if (month < 1 || day < 1) { rb_raise(cMysql2Error, "Invalid date: %s", row[i]); val = Qnil; } else { val = rb_funcall(cDate, intern_new, 3, INT2NUM(year), INT2NUM(month), INT2NUM(day)); } } break; } case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_STRING: // CHAR or BINARY field case MYSQL_TYPE_SET: // SET field case MYSQL_TYPE_ENUM: // ENUM field case MYSQL_TYPE_GEOMETRY: // Spatial fielda default: val = rb_str_new(row[i], fieldLengths[i]); #ifdef HAVE_RUBY_ENCODING_H // if binary flag is set, respect it's wishes if (fields[i].flags & BINARY_FLAG) { rb_enc_associate(val, binaryEncoding); } else { // lookup the encoding configured on this field VALUE new_encoding = rb_funcall(cMysql2Client, intern_encoding_from_charset_code, 1, INT2NUM(fields[i].charsetnr)); if (new_encoding != Qnil) { // use the field encoding we were able to match rb_encoding *enc = rb_to_encoding(new_encoding); rb_enc_associate(val, enc); } else { // otherwise fall-back to the connection's encoding rb_enc_associate(val, conn_enc); } if (default_internal_enc) { val = rb_str_export_to_enc(val, default_internal_enc); } } #endif break; } rb_hash_aset(rowHash, field, val); } else { rb_hash_aset(rowHash, field, Qnil); } } return rowHash; }
static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) { struct nogvl_send_query_args args; fd_set fdset; int fd, retval; int async = 0; VALUE opts, defaults, read_timeout; #ifdef HAVE_RUBY_ENCODING_H rb_encoding *conn_enc; #endif struct timeval tv; struct timeval* tvp; long int sec; VALUE result; GET_CLIENT(self); REQUIRE_OPEN_DB(wrapper); args.mysql = wrapper->client; // see if this connection is still waiting on a result from a previous query if (wrapper->active == 0) { // mark this connection active wrapper->active = 1; } else { rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result"); } defaults = rb_iv_get(self, "@query_options"); if (rb_scan_args(argc, argv, "11", &args.sql, &opts) == 2) { opts = rb_funcall(defaults, intern_merge, 1, opts); rb_iv_set(self, "@query_options", opts); if (rb_hash_aref(opts, sym_async) == Qtrue) { async = 1; } } else { opts = defaults; } Check_Type(args.sql, T_STRING); #ifdef HAVE_RUBY_ENCODING_H conn_enc = rb_to_encoding(wrapper->encoding); // ensure the string is in the encoding the connection is expecting args.sql = rb_str_export_to_enc(args.sql, conn_enc); #endif if (rb_thread_blocking_region(nogvl_send_query, &args, RUBY_UBF_IO, 0) == Qfalse) { // an error occurred, we're not active anymore MARK_CONN_INACTIVE(self); return rb_raise_mysql2_error(wrapper); } read_timeout = rb_iv_get(self, "@read_timeout"); tvp = NULL; if (!NIL_P(read_timeout)) { Check_Type(read_timeout, T_FIXNUM); tvp = &tv; sec = FIX2INT(read_timeout); // TODO: support partial seconds? // also, this check is here for sanity, we also check up in Ruby if (sec >= 0) { tvp->tv_sec = sec; } else { rb_raise(cMysql2Error, "read_timeout must be a positive integer, you passed %ld", sec); } tvp->tv_usec = 0; } if (!async) { // the below code is largely from do_mysql // http://github.com/datamapper/do fd = wrapper->client->net.fd; for(;;) { int fd_set_fd = fd; #if defined(_WIN32) && !defined(HAVE_RB_THREAD_BLOCKING_REGION) WSAPROTOCOL_INFO wsa_pi; // dupicate the SOCKET from libmysql int r = WSADuplicateSocket(fd, GetCurrentProcessId(), &wsa_pi); SOCKET s = WSASocket(wsa_pi.iAddressFamily, wsa_pi.iSocketType, wsa_pi.iProtocol, &wsa_pi, 0, 0); // create the CRT fd so ruby can get back to the SOCKET fd_set_fd = _open_osfhandle(s, O_RDWR|O_BINARY); #endif FD_ZERO(&fdset); FD_SET(fd_set_fd, &fdset); retval = rb_thread_select(fd_set_fd + 1, &fdset, NULL, NULL, tvp); #if defined(_WIN32) && !defined(HAVE_RB_THREAD_BLOCKING_REGION) // cleanup the CRT fd _close(fd_set_fd); // cleanup the duplicated SOCKET closesocket(s); #endif if (retval == 0) { rb_raise(cMysql2Error, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout)); } if (retval < 0) { rb_sys_fail(0); } if (retval > 0) { break; } } result = rb_mysql_client_async_result(self); return result; } else { return Qnil; } }
/* call-seq: stmt.execute * * Executes the current prepared statement, returns +stmt+. */ static VALUE execute(int argc, VALUE *argv, VALUE self) { MYSQL_STMT *stmt; MYSQL_BIND *bind_buffers; unsigned long bind_count; long i; Data_Get_Struct(self, MYSQL_STMT, stmt); bind_count = mysql_stmt_param_count(stmt); if (argc != (long)bind_count) { rb_raise(cMysql2Error, "Bind parameter count (%ld) doesn't match number of arguments (%d)", bind_count, argc); } // setup any bind variables in the query if (bind_count > 0) { bind_buffers = xcalloc(bind_count, sizeof(MYSQL_BIND)); for (i = 0; i < argc; i++) { bind_buffers[i].buffer = NULL; switch (TYPE(argv[i])) { case T_NIL: bind_buffers[i].buffer_type = MYSQL_TYPE_NULL; break; case T_FIXNUM: #if SIZEOF_INT < SIZEOF_LONG bind_buffers[i].buffer_type = MYSQL_TYPE_LONGLONG; bind_buffers[i].buffer = malloc(sizeof(long long int)); *(long*)(bind_buffers[i].buffer) = FIX2LONG(argv[i]); #else bind_buffers[i].buffer_type = MYSQL_TYPE_LONG; bind_buffers[i].buffer = malloc(sizeof(int)); *(long*)(bind_buffers[i].buffer) = FIX2INT(argv[i]); #endif break; case T_BIGNUM: bind_buffers[i].buffer_type = MYSQL_TYPE_LONGLONG; bind_buffers[i].buffer = malloc(sizeof(long long int)); *(LONG_LONG*)(bind_buffers[i].buffer) = rb_big2ll(argv[i]); break; case T_FLOAT: bind_buffers[i].buffer_type = MYSQL_TYPE_DOUBLE; bind_buffers[i].buffer = malloc(sizeof(double)); *(double*)(bind_buffers[i].buffer) = NUM2DBL(argv[i]); break; case T_STRING: bind_buffers[i].buffer_type = MYSQL_TYPE_STRING; bind_buffers[i].buffer = RSTRING_PTR(argv[i]); bind_buffers[i].buffer_length = RSTRING_LEN(argv[i]); unsigned long *len = malloc(sizeof(long)); (*len) = RSTRING_LEN(argv[i]); bind_buffers[i].length = len; break; default: // TODO: what Ruby type should support MYSQL_TYPE_TIME if (CLASS_OF(argv[i]) == rb_cTime || CLASS_OF(argv[i]) == cDateTime) { bind_buffers[i].buffer_type = MYSQL_TYPE_DATETIME; bind_buffers[i].buffer = malloc(sizeof(MYSQL_TIME)); MYSQL_TIME t; VALUE rb_time = argv[i]; memset(&t, 0, sizeof(MYSQL_TIME)); t.second_part = 0; t.neg = 0; t.second = FIX2INT(rb_funcall(rb_time, rb_intern("sec"), 0)); t.minute = FIX2INT(rb_funcall(rb_time, rb_intern("min"), 0)); t.hour = FIX2INT(rb_funcall(rb_time, rb_intern("hour"), 0)); t.day = FIX2INT(rb_funcall(rb_time, rb_intern("day"), 0)); t.month = FIX2INT(rb_funcall(rb_time, rb_intern("month"), 0)); t.year = FIX2INT(rb_funcall(rb_time, rb_intern("year"), 0)); *(MYSQL_TIME*)(bind_buffers[i].buffer) = t; } else if (CLASS_OF(argv[i]) == cDate) { bind_buffers[i].buffer_type = MYSQL_TYPE_DATE; bind_buffers[i].buffer = malloc(sizeof(MYSQL_TIME)); MYSQL_TIME t; VALUE rb_time = argv[i]; memset(&t, 0, sizeof(MYSQL_TIME)); t.second_part = 0; t.neg = 0; t.day = FIX2INT(rb_funcall(rb_time, rb_intern("day"), 0)); t.month = FIX2INT(rb_funcall(rb_time, rb_intern("month"), 0)); t.year = FIX2INT(rb_funcall(rb_time, rb_intern("year"), 0)); *(MYSQL_TIME*)(bind_buffers[i].buffer) = t; } else if (CLASS_OF(argv[i]) == cBigDecimal) { bind_buffers[i].buffer_type = MYSQL_TYPE_NEWDECIMAL; } break; } } // copies bind_buffers into internal storage if (mysql_stmt_bind_param(stmt, bind_buffers)) { FREE_BINDS; rb_raise(cMysql2Error, "%s", mysql_stmt_error(stmt)); } } if (rb_thread_blocking_region(nogvl_execute, stmt, RUBY_UBF_IO, 0) == Qfalse) { FREE_BINDS; rb_raise(cMysql2Error, "%s", mysql_stmt_error(stmt)); } if (bind_count > 0) { FREE_BINDS; } return self; }
/* ruby 1.9 */ sword oci8_call_without_gvl(oci8_svcctx_t *svcctx, void *(*func)(void *), void *data) { OCIError *errhp = oci8_errhp; if (!NIL_P(svcctx->executing_thread)) { rb_raise(rb_eRuntimeError /* FIXME */, "executing in another thread"); } if (!svcctx->suppress_free_temp_lobs) { oci8_temp_lob_t *lob = svcctx->temp_lobs; while (lob != NULL) { oci8_temp_lob_t *lob_next = lob->next; if (svcctx->non_blocking) { free_temp_lob_arg_t arg; sword rv; arg.svcctx = svcctx; arg.svchp = svcctx->base.hp.svc; arg.errhp = errhp; arg.lob = lob->lob; svcctx->executing_thread = rb_thread_current(); #ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL rv = (sword)(VALUE)rb_thread_call_without_gvl(free_temp_lob, &arg, oci8_unblock_func, svcctx); #else rv = (sword)rb_thread_blocking_region((VALUE(*)(void*))free_temp_lob, &arg, oci8_unblock_func, svcctx); #endif if (rv == OCI_ERROR) { if (oci8_get_error_code(errhp) == 1013) { rb_raise(eOCIBreak, "Canceled by user request."); } } } else { OCILobFreeTemporary(svcctx->base.hp.svc, errhp, lob->lob); } OCIDescriptorFree(lob->lob, OCI_DTYPE_LOB); xfree(lob); svcctx->temp_lobs = lob = lob_next; } } if (svcctx->non_blocking) { sword rv; svcctx->executing_thread = rb_thread_current(); /* Note: executing_thread is cleard at the end of the blocking function. */ #ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL rv = (sword)(VALUE)rb_thread_call_without_gvl(func, data, oci8_unblock_func, svcctx); #else rv = (sword)rb_thread_blocking_region((VALUE(*)(void*))func, data, oci8_unblock_func, svcctx); #endif if (rv == OCI_ERROR) { if (oci8_get_error_code(errhp) == 1013) { rb_raise(eOCIBreak, "Canceled by user request."); } } return rv; } else { return (sword)(VALUE)func(data); } }
static VALUE rb_monitor_run_bang(VALUE self) { BOOL already_running, waiting_succeeded; WDM_PMonitor monitor; WDM_DEBUG("Running the monitor!"); Data_Get_Struct(self, WDM_Monitor, monitor); already_running = FALSE; EnterCriticalSection(&monitor->lock); if ( monitor->running ) { already_running = TRUE; } else { monitor->running = TRUE; } LeaveCriticalSection(&monitor->lock); if (already_running) { WDM_DEBUG("Not doing anything because the monitor is already running!"); return Qnil; } // Reset events ResetEvent(monitor->process_event); ResetEvent(monitor->stop_event); monitor->monitoring_thread = CreateThread( NULL, // default security attributes 0, // use default stack size start_monitoring, // thread function name monitor, // argument to thread function 0, // use default creation flags NULL // Ignore thread identifier ); if ( monitor->monitoring_thread == NULL ) { rb_raise(eWDM_Error, "Can't create a thread for the monitor!"); } while ( monitor->running ) { waiting_succeeded = rb_thread_blocking_region(wait_for_changes, monitor->process_event, stop_monitoring, monitor); if ( waiting_succeeded == Qfalse ) { rb_raise(eWDM_Error, "Failed while waiting for a change in the watched directories!"); } if ( ! monitor->running ) { wdm_queue_empty(monitor->changes); return Qnil; } process_changes(monitor->changes); if ( ! ResetEvent(monitor->process_event) ) { rb_raise(eWDM_Error, "Couldn't reset system events to watch for changes!"); } } return Qnil; }
static VALUE loop_run_poll(VALUE argp) { lp_arg *args = (lp_arg*)argp; rb_mt_loop *loop = args->loop; hrtime_t now, next_time; if (loop->events.count) { uint32_t i; args->fds = calloc(loop->events.count, sizeof(struct pollfd)); if (args->fds == NULL) { rb_raise(cb_eClientNoMemoryError, "failed to allocate memory for pollfd"); } for(i = 0; i < loop->events.count; i++) { rb_mt_socket_list *list = &loop->events.sockets[i]; args->fds[i].fd = list->socket; args->fds[i].events = (list->flags & LCB_READ_EVENT ? POLLIN : 0) | (list->flags & LCB_WRITE_EVENT ? POLLOUT : 0); } args->nfd = loop->events.count; } retry: next_time = timers_minimum(&loop->timers); if (next_time) { now = gethrtime(); args->ts = next_time <= now ? 0 : next_time - now; } else { args->ts = HRTIME_INFINITY; } #ifdef HAVE_RB_THREAD_BLOCKING_REGION rb_thread_blocking_region(loop_blocking_poll, args, RUBY_UBF_PROCESS, NULL); #else if (rb_thread_alone()) { TRAP_BEG; args->result = xpoll(args->fds, args->nfd, args->ts); if (args->result < 0) args->lerrno = errno; TRAP_END; } else { /* 5 millisecond pause */ hrtime_t mini_pause = 5000000; int exact = 0; if (args->ts != HRTIME_INFINITY && args->ts < mini_pause) { mini_pause = args->ts; exact = 1; } TRAP_BEG; args->result = xpoll(args->fds, args->nfd, mini_pause); if (args->result < 0) args->lerrno = errno; TRAP_END; if (args->result == 0 && !exact) { args->result = -1; args->lerrno = EINTR; } } #endif if (args->result < 0) { errno = args->lerrno; switch (errno) { case EINTR: #ifdef ERESTART case ERESTART: #endif #ifndef HAVE_RB_THREAD_BLOCKING_REGION rb_thread_schedule(); #endif goto retry; } rb_sys_fail("poll"); return Qnil; } if (next_time) { now = gethrtime(); } if (args->result > 0) { uint32_t cnt = args->result; uint32_t fd_n = 0, ev_n = 0; while (cnt && fd_n < args->nfd && ev_n < loop->events.count) { struct pollfd *res = args->fds + fd_n; rb_mt_socket_list *list = loop->events.sockets + ev_n; rb_mt_event *sock = list->first; /* if plugin used correctly, this checks are noop */ if (res->fd < list->socket) { fd_n++; continue; } else if (res->fd > list->socket) { ev_n++; continue; } if (res->revents) { short flags = ((res->revents & POLLIN_SET) ? LCB_READ_EVENT : 0) | ((res->revents & POLLOUT_SET) ? LCB_WRITE_EVENT : 0); cnt--; loop_enque_events(&loop->callbacks, sock, flags); } fd_n++; ev_n++; } callbacks_run(&loop->callbacks); } if (next_time) { timers_run(&loop->timers, now); } if (loop->events.count == 0 && loop->timers.count == 0) { loop->run = 0; } return Qnil; }
static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) { struct nogvl_send_query_args args; fd_set fdset; int fd, retval; int async = 0; VALUE opts, defaults, read_timeout; GET_CLIENT(self); REQUIRE_OPEN_DB(wrapper); args.mysql = wrapper->client; // see if this connection is still waiting on a result from a previous query if (wrapper->active == 0) { // mark this connection active wrapper->active = 1; } else { rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result"); } defaults = rb_iv_get(self, "@query_options"); if (rb_scan_args(argc, argv, "11", &args.sql, &opts) == 2) { opts = rb_funcall(defaults, intern_merge, 1, opts); rb_iv_set(self, "@query_options", opts); if (rb_hash_aref(opts, sym_async) == Qtrue) { async = 1; } } else { opts = defaults; } Check_Type(args.sql, T_STRING); #ifdef HAVE_RUBY_ENCODING_H rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding); // ensure the string is in the encoding the connection is expecting args.sql = rb_str_export_to_enc(args.sql, conn_enc); #endif if (rb_thread_blocking_region(nogvl_send_query, &args, RUBY_UBF_IO, 0) == Qfalse) { // an error occurred, we're not active anymore MARK_CONN_INACTIVE(self); return rb_raise_mysql2_error(wrapper->client); } read_timeout = rb_iv_get(self, "@read_timeout"); struct timeval tv; struct timeval* tvp = NULL; if (!NIL_P(read_timeout)) { Check_Type(read_timeout, T_FIXNUM); tvp = &tv; long int sec = FIX2INT(read_timeout); // TODO: support partial seconds? // also, this check is here for sanity, we also check up in Ruby if (sec >= 0) { tvp->tv_sec = sec; } else { rb_raise(cMysql2Error, "read_timeout must be a positive integer, you passed %d", sec); } tvp->tv_usec = 0; } if (!async) { // the below code is largely from do_mysql // http://github.com/datamapper/do fd = wrapper->client->net.fd; for(;;) { FD_ZERO(&fdset); FD_SET(fd, &fdset); retval = rb_thread_select(fd + 1, &fdset, NULL, NULL, tvp); if (retval == 0) { rb_raise(cMysql2Error, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout)); } if (retval < 0) { rb_sys_fail(0); } if (retval > 0) { break; } } VALUE result = rb_mysql_client_async_result(self); return result; } else { return Qnil; } }
static int sedna_non_blocking_execute(SQ *q) { return rb_thread_blocking_region((void*)sedna_blocking_execute, q, RUBY_UBF_IO, NULL); }
static int sedna_non_blocking_connect(SCA *c) { return rb_thread_blocking_region((void*)sedna_blocking_connect, c, RUBY_UBF_IO, NULL); }
static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezone, int symbolizeKeys, int asArray, int castBool) { VALUE rowVal; mysql2_result_wrapper * wrapper; MYSQL_ROW row; MYSQL_FIELD * fields = NULL; unsigned int i = 0; unsigned long * fieldLengths; void * ptr; GetMysql2Result(self, wrapper); #ifdef HAVE_RUBY_ENCODING_H rb_encoding *default_internal_enc = rb_default_internal_encoding(); rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding); #endif ptr = wrapper->result; row = (MYSQL_ROW)rb_thread_blocking_region(nogvl_fetch_row, ptr, RUBY_UBF_IO, 0); if (row == NULL) { return Qnil; } if (asArray) { rowVal = rb_ary_new2(wrapper->numberOfFields); } else { rowVal = rb_hash_new(); } fields = mysql_fetch_fields(wrapper->result); fieldLengths = mysql_fetch_lengths(wrapper->result); if (wrapper->fields == Qnil) { wrapper->numberOfFields = mysql_num_fields(wrapper->result); wrapper->fields = rb_ary_new2(wrapper->numberOfFields); } for (i = 0; i < wrapper->numberOfFields; i++) { VALUE field = rb_mysql_result_fetch_field(self, i, symbolizeKeys); if (row[i]) { VALUE val = Qnil; switch(fields[i].type) { case MYSQL_TYPE_NULL: // NULL-type field val = Qnil; break; case MYSQL_TYPE_BIT: // BIT field (MySQL 5.0.3 and up) val = rb_str_new(row[i], fieldLengths[i]); break; case MYSQL_TYPE_TINY: // TINYINT field if (castBool && fields[i].length == 1) { val = *row[i] == '1' ? Qtrue : Qfalse; break; } case MYSQL_TYPE_SHORT: // SMALLINT field case MYSQL_TYPE_LONG: // INTEGER field case MYSQL_TYPE_INT24: // MEDIUMINT field case MYSQL_TYPE_LONGLONG: // BIGINT field case MYSQL_TYPE_YEAR: // YEAR field val = rb_cstr2inum(row[i], 10); break; case MYSQL_TYPE_DECIMAL: // DECIMAL or NUMERIC field case MYSQL_TYPE_NEWDECIMAL: // Precision math DECIMAL or NUMERIC field (MySQL 5.0.3 and up) if (strtod(row[i], NULL) == 0.000000){ val = rb_funcall(cBigDecimal, intern_new, 1, opt_decimal_zero); }else{ val = rb_funcall(cBigDecimal, intern_new, 1, rb_str_new(row[i], fieldLengths[i])); } break; case MYSQL_TYPE_FLOAT: // FLOAT field case MYSQL_TYPE_DOUBLE: { // DOUBLE or REAL field double column_to_double; column_to_double = strtod(row[i], NULL); if (column_to_double == 0.000000){ val = opt_float_zero; }else{ val = rb_float_new(column_to_double); } break; } case MYSQL_TYPE_TIME: { // TIME field int hour, min, sec, tokens; tokens = sscanf(row[i], "%2d:%2d:%2d", &hour, &min, &sec); val = rb_funcall(rb_cTime, db_timezone, 6, opt_time_year, opt_time_month, opt_time_month, INT2NUM(hour), INT2NUM(min), INT2NUM(sec)); if (!NIL_P(app_timezone)) { if (app_timezone == intern_local) { val = rb_funcall(val, intern_localtime, 0); } else { // utc val = rb_funcall(val, intern_utc, 0); } } break; } case MYSQL_TYPE_TIMESTAMP: // TIMESTAMP field case MYSQL_TYPE_DATETIME: { // DATETIME field int year, month, day, hour, min, sec, tokens; tokens = sscanf(row[i], "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &min, &sec); if (year+month+day+hour+min+sec == 0) { val = Qnil; } else { if (month < 1 || day < 1) { rb_raise(cMysql2Error, "Invalid date: %s", row[i]); val = Qnil; } else { if (year < 1902 || year+month+day > 2058) { // use DateTime instead VALUE offset = INT2NUM(0); if (db_timezone == intern_local) { offset = rb_funcall(cMysql2Client, intern_local_offset, 0); } val = rb_funcall(cDateTime, intern_civil, 7, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec), offset); if (!NIL_P(app_timezone)) { if (app_timezone == intern_local) { offset = rb_funcall(cMysql2Client, intern_local_offset, 0); val = rb_funcall(val, intern_new_offset, 1, offset); } else { // utc val = rb_funcall(val, intern_new_offset, 1, opt_utc_offset); } } } else { val = rb_funcall(rb_cTime, db_timezone, 6, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec)); if (!NIL_P(app_timezone)) { if (app_timezone == intern_local) { val = rb_funcall(val, intern_localtime, 0); } else { // utc val = rb_funcall(val, intern_utc, 0); } } } } } break; } case MYSQL_TYPE_DATE: // DATE field case MYSQL_TYPE_NEWDATE: { // Newer const used > 5.0 int year, month, day, tokens; tokens = sscanf(row[i], "%4d-%2d-%2d", &year, &month, &day); if (year+month+day == 0) { val = Qnil; } else { if (month < 1 || day < 1) { rb_raise(cMysql2Error, "Invalid date: %s", row[i]); val = Qnil; } else { val = rb_funcall(cDate, intern_new, 3, INT2NUM(year), INT2NUM(month), INT2NUM(day)); } } break; } case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_STRING: // CHAR or BINARY field case MYSQL_TYPE_SET: // SET field case MYSQL_TYPE_ENUM: // ENUM field case MYSQL_TYPE_GEOMETRY: // Spatial fielda default: val = rb_str_new(row[i], fieldLengths[i]); #ifdef HAVE_RUBY_ENCODING_H // if binary flag is set, respect it's wishes if (fields[i].flags & BINARY_FLAG) { rb_enc_associate(val, binaryEncoding); } else { // lookup the encoding configured on this field VALUE new_encoding = rb_funcall(cMysql2Client, intern_encoding_from_charset_code, 1, INT2NUM(fields[i].charsetnr)); if (new_encoding != Qnil) { // use the field encoding we were able to match rb_encoding *enc = rb_to_encoding(new_encoding); rb_enc_associate(val, enc); } else { // otherwise fall-back to the connection's encoding rb_enc_associate(val, conn_enc); } if (default_internal_enc) { val = rb_str_export_to_enc(val, default_internal_enc); } } #endif break; } if (asArray) { rb_ary_push(rowVal, val); } else { rb_hash_aset(rowVal, field, val); } } else { if (asArray) { rb_ary_push(rowVal, Qnil); } else { rb_hash_aset(rowVal, field, Qnil); } } } return rowVal; }
static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) { #ifndef _WIN32 struct async_query_args async_args; #endif struct nogvl_send_query_args args; int async = 0; VALUE opts, defaults; #ifdef HAVE_RUBY_ENCODING_H rb_encoding *conn_enc; #endif GET_CLIENT(self); REQUIRE_OPEN_DB(wrapper); args.mysql = wrapper->client; // see if this connection is still waiting on a result from a previous query if (wrapper->active == 0) { // mark this connection active wrapper->active = 1; } else { rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result"); } defaults = rb_iv_get(self, "@query_options"); if (rb_scan_args(argc, argv, "11", &args.sql, &opts) == 2) { opts = rb_funcall(defaults, intern_merge, 1, opts); rb_iv_set(self, "@query_options", opts); if (rb_hash_aref(opts, sym_async) == Qtrue) { async = 1; } } else { opts = defaults; } Check_Type(args.sql, T_STRING); #ifdef HAVE_RUBY_ENCODING_H conn_enc = rb_to_encoding(wrapper->encoding); // ensure the string is in the encoding the connection is expecting args.sql = rb_str_export_to_enc(args.sql, conn_enc); #endif if (rb_thread_blocking_region(nogvl_send_query, &args, RUBY_UBF_IO, 0) == Qfalse) { // an error occurred, we're not active anymore MARK_CONN_INACTIVE(self); return rb_raise_mysql2_error(wrapper); } #ifndef _WIN32 if (!async) { async_args.fd = wrapper->client->net.fd; async_args.self = self; rb_rescue2(do_query, (VALUE)&async_args, disconnect_and_raise, self, rb_eException, (VALUE)0); return rb_mysql_client_async_result(self); } else { return Qnil; } #else // this will just block until the result is ready return rb_ensure(rb_mysql_client_async_result, self, finish_and_mark_inactive, self); #endif }
static VALUE rb_mysql_client_ping(VALUE self) { GET_CLIENT(self); return rb_thread_blocking_region(nogvl_ping, wrapper->client, RUBY_UBF_IO, 0); }
static VALUE f_generic_writev(VALUE fd, VALUE *array_of_components, unsigned int count) { VALUE components, str; unsigned int total_size, total_components, ngroups; IOVectorGroup *groups; unsigned int i, j, group_offset, vector_offset; unsigned long long ssize_max; ssize_t ret; int done, fd_num, e; #ifndef TRAP_BEG WritevWrapperData writev_wrapper_data; #endif /* First determine the number of components that we have. */ total_components = 0; for (i = 0; i < count; i++) { Check_Type(array_of_components[i], T_ARRAY); total_components += (unsigned int) RARRAY_LEN(array_of_components[i]); } if (total_components == 0) { return NUM2INT(0); } /* A single writev() call can only accept IOV_MAX vectors, so we * may have to split the components into groups and perform * multiple writev() calls, one per group. Determine the number * of groups needed, how big each group should be and allocate * memory for them. */ if (total_components % IOV_MAX == 0) { ngroups = total_components / IOV_MAX; groups = alloca(ngroups * sizeof(IOVectorGroup)); if (groups == NULL) { rb_raise(rb_eNoMemError, "Insufficient stack space."); } memset(groups, 0, ngroups * sizeof(IOVectorGroup)); for (i = 0; i < ngroups; i++) { groups[i].io_vectors = alloca(IOV_MAX * sizeof(struct iovec)); if (groups[i].io_vectors == NULL) { rb_raise(rb_eNoMemError, "Insufficient stack space."); } groups[i].count = IOV_MAX; } } else { ngroups = total_components / IOV_MAX + 1; groups = alloca(ngroups * sizeof(IOVectorGroup)); if (groups == NULL) { rb_raise(rb_eNoMemError, "Insufficient stack space."); } memset(groups, 0, ngroups * sizeof(IOVectorGroup)); for (i = 0; i < ngroups - 1; i++) { groups[i].io_vectors = alloca(IOV_MAX * sizeof(struct iovec)); if (groups[i].io_vectors == NULL) { rb_raise(rb_eNoMemError, "Insufficient stack space."); } groups[i].count = IOV_MAX; } groups[ngroups - 1].io_vectors = alloca((total_components % IOV_MAX) * sizeof(struct iovec)); if (groups[ngroups - 1].io_vectors == NULL) { rb_raise(rb_eNoMemError, "Insufficient stack space."); } groups[ngroups - 1].count = total_components % IOV_MAX; } /* Now distribute the components among the groups, filling the iovec * array in each group. Also calculate the total data size while we're * at it. */ total_size = 0; group_offset = 0; vector_offset = 0; for (i = 0; i < count; i++) { components = array_of_components[i]; for (j = 0; j < (unsigned int) RARRAY_LEN(components); j++) { str = rb_ary_entry(components, j); str = rb_obj_as_string(str); total_size += (unsigned int) RSTRING_LEN(str); /* I know writev() doesn't write to iov_base, but on some * platforms it's still defined as non-const char * * :-( */ groups[group_offset].io_vectors[vector_offset].iov_base = (char *) RSTRING_PTR(str); groups[group_offset].io_vectors[vector_offset].iov_len = RSTRING_LEN(str); groups[group_offset].total_size += RSTRING_LEN(str); vector_offset++; if (vector_offset == groups[group_offset].count) { group_offset++; vector_offset = 0; } } } /* We don't compare to SSIZE_MAX directly in order to shut up a compiler warning on OS X Snow Leopard. */ ssize_max = SSIZE_MAX; if (total_size > ssize_max) { rb_raise(rb_eArgError, "The total size of the components may not be larger than SSIZE_MAX."); } /* Write the data. */ fd_num = NUM2INT(fd); for (i = 0; i < ngroups; i++) { /* Wait until the file descriptor becomes writable before writing things. */ rb_thread_fd_writable(fd_num); done = 0; while (!done) { #ifdef TRAP_BEG TRAP_BEG; ret = writev(fd_num, groups[i].io_vectors, groups[i].count); TRAP_END; #else writev_wrapper_data.filedes = fd_num; writev_wrapper_data.iov = groups[i].io_vectors; writev_wrapper_data.iovcnt = groups[i].count; ret = (int) rb_thread_blocking_region(writev_wrapper, &writev_wrapper_data, RUBY_UBF_IO, 0); #endif if (ret == -1) { /* If the error is something like EAGAIN, yield to another * thread until the file descriptor becomes writable again. * In case of other errors, raise an exception. */ if (!rb_io_wait_writable(fd_num)) { rb_sys_fail("writev()"); } } else if (ret < groups[i].total_size) { /* Not everything in this group has been written. Retry without * writing the bytes that been successfully written. */ e = errno; update_group_written_info(&groups[i], ret); errno = e; rb_io_wait_writable(fd_num); } else { done = 1; } } } return INT2NUM(total_size); }
static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezone, int symbolizeKeys, int asArray, int castBool, int cast, MYSQL_FIELD * fields) { VALUE rowVal; mysql2_result_wrapper * wrapper; MYSQL_ROW row; unsigned int i = 0; unsigned long * fieldLengths; void * ptr; #ifdef HAVE_RUBY_ENCODING_H rb_encoding *default_internal_enc; rb_encoding *conn_enc; #endif GetMysql2Result(self, wrapper); #ifdef HAVE_RUBY_ENCODING_H default_internal_enc = rb_default_internal_encoding(); conn_enc = rb_to_encoding(wrapper->encoding); #endif ptr = wrapper->result; row = (MYSQL_ROW)rb_thread_blocking_region(nogvl_fetch_row, ptr, RUBY_UBF_IO, 0); if (row == NULL) { return Qnil; } if (asArray) { rowVal = rb_ary_new2(wrapper->numberOfFields); } else { rowVal = rb_hash_new(); } fieldLengths = mysql_fetch_lengths(wrapper->result); if (wrapper->fields == Qnil) { wrapper->numberOfFields = mysql_num_fields(wrapper->result); wrapper->fields = rb_ary_new2(wrapper->numberOfFields); } for (i = 0; i < wrapper->numberOfFields; i++) { VALUE field = rb_mysql_result_fetch_field(self, i, symbolizeKeys); if (row[i]) { VALUE val = Qnil; enum enum_field_types type = fields[i].type; if(!cast) { if (type == MYSQL_TYPE_NULL) { val = Qnil; } else { val = rb_str_new(row[i], fieldLengths[i]); #ifdef HAVE_RUBY_ENCODING_H val = mysql2_set_field_string_encoding(val, fields[i], default_internal_enc, conn_enc); #endif } } else { switch(type) { case MYSQL_TYPE_NULL: // NULL-type field val = Qnil; break; case MYSQL_TYPE_BIT: // BIT field (MySQL 5.0.3 and up) val = rb_str_new(row[i], fieldLengths[i]); break; case MYSQL_TYPE_TINY: // TINYINT field if (castBool && fields[i].length == 1) { val = *row[i] == '1' ? Qtrue : Qfalse; break; } case MYSQL_TYPE_SHORT: // SMALLINT field case MYSQL_TYPE_LONG: // INTEGER field case MYSQL_TYPE_INT24: // MEDIUMINT field case MYSQL_TYPE_LONGLONG: // BIGINT field case MYSQL_TYPE_YEAR: // YEAR field val = rb_cstr2inum(row[i], 10); break; case MYSQL_TYPE_DECIMAL: // DECIMAL or NUMERIC field case MYSQL_TYPE_NEWDECIMAL: // Precision math DECIMAL or NUMERIC field (MySQL 5.0.3 and up) if (fields[i].decimals == 0) { val = rb_cstr2inum(row[i], 10); } else if (strtod(row[i], NULL) == 0.000000){ val = rb_funcall(cBigDecimal, intern_new, 1, opt_decimal_zero); }else{ val = rb_funcall(cBigDecimal, intern_new, 1, rb_str_new(row[i], fieldLengths[i])); } break; case MYSQL_TYPE_FLOAT: // FLOAT field case MYSQL_TYPE_DOUBLE: { // DOUBLE or REAL field double column_to_double; column_to_double = strtod(row[i], NULL); if (column_to_double == 0.000000){ val = opt_float_zero; }else{ val = rb_float_new(column_to_double); } break; } case MYSQL_TYPE_TIME: { // TIME field int hour, min, sec, tokens; tokens = sscanf(row[i], "%2d:%2d:%2d", &hour, &min, &sec); val = rb_funcall(rb_cTime, db_timezone, 6, opt_time_year, opt_time_month, opt_time_month, INT2NUM(hour), INT2NUM(min), INT2NUM(sec)); if (!NIL_P(app_timezone)) { if (app_timezone == intern_local) { val = rb_funcall(val, intern_localtime, 0); } else { // utc val = rb_funcall(val, intern_utc, 0); } } break; } case MYSQL_TYPE_TIMESTAMP: // TIMESTAMP field case MYSQL_TYPE_DATETIME: { // DATETIME field unsigned int year, month, day, hour, min, sec, tokens; uint64_t seconds; tokens = sscanf(row[i], "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &min, &sec); seconds = (year*31557600ULL) + (month*2592000ULL) + (day*86400ULL) + (hour*3600ULL) + (min*60ULL) + sec; if (seconds == 0) { val = Qnil; } else { if (month < 1 || day < 1) { rb_raise(cMysql2Error, "Invalid date: %s", row[i]); val = Qnil; } else { if (seconds < MYSQL2_MIN_TIME || seconds >= MYSQL2_MAX_TIME) { // use DateTime instead VALUE offset = INT2NUM(0); if (db_timezone == intern_local) { offset = rb_funcall(cMysql2Client, intern_local_offset, 0); } val = rb_funcall(cDateTime, intern_civil, 7, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec), offset); if (!NIL_P(app_timezone)) { if (app_timezone == intern_local) { offset = rb_funcall(cMysql2Client, intern_local_offset, 0); val = rb_funcall(val, intern_new_offset, 1, offset); } else { // utc val = rb_funcall(val, intern_new_offset, 1, opt_utc_offset); } } } else { val = rb_funcall(rb_cTime, db_timezone, 6, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec)); if (!NIL_P(app_timezone)) { if (app_timezone == intern_local) { val = rb_funcall(val, intern_localtime, 0); } else { // utc val = rb_funcall(val, intern_utc, 0); } } } } } break; } case MYSQL_TYPE_DATE: // DATE field case MYSQL_TYPE_NEWDATE: { // Newer const used > 5.0 int year, month, day, tokens; tokens = sscanf(row[i], "%4d-%2d-%2d", &year, &month, &day); if (year+month+day == 0) { val = Qnil; } else { if (month < 1 || day < 1) { rb_raise(cMysql2Error, "Invalid date: %s", row[i]); val = Qnil; } else { val = rb_funcall(cDate, intern_new, 3, INT2NUM(year), INT2NUM(month), INT2NUM(day)); } } break; } case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_STRING: // CHAR or BINARY field case MYSQL_TYPE_SET: // SET field case MYSQL_TYPE_ENUM: // ENUM field case MYSQL_TYPE_GEOMETRY: // Spatial fielda default: val = rb_str_new(row[i], fieldLengths[i]); #ifdef HAVE_RUBY_ENCODING_H val = mysql2_set_field_string_encoding(val, fields[i], default_internal_enc, conn_enc); #endif break; } } if (asArray) { rb_ary_push(rowVal, val); } else { rb_hash_aset(rowVal, field, val); } } else { if (asArray) { rb_ary_push(rowVal, Qnil); } else { rb_hash_aset(rowVal, field, Qnil); } } } return rowVal; }
/* * IO.writev(fd, %w(hello world)) * * This method writes the contents of an array of strings to the given +fd+. * It can be useful to avoid generating a temporary string via Array#join * when writing out large arrays of strings. * * The given array should have fewer elements than the IO::IOV_MAX constant. * * Returns the number of bytes written. */ static VALUE s_io_writev(VALUE klass, VALUE fd, VALUE ary) { ssize_t result = 0; ssize_t left; struct writev_args args; args.fd = NUM2INT(fd); ARY2IOVEC(args.iov, args.iovcnt, left, ary); for(;;) { #ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL ssize_t w = (ssize_t)rb_thread_call_without_gvl( (void*)nogvl_writev, &args, RUBY_UBF_IO, 0 ); #else ssize_t w = (ssize_t)rb_thread_blocking_region( nogvl_writev, &args, RUBY_UBF_IO, 0 ); #endif if(w == -1) { if (rb_io_wait_writable(args.fd)) { continue; } else { if (result > 0) { /* * unlikely to hit this case, return the already written bytes, * we'll let the next write (or close) fail instead */ break; } rb_sys_fail("writev"); } } result += w; if(w == left) { break; } else { /* partial write, this can get tricky */ int i; struct iovec *new_iov = args.iov; left -= w; /* skip over iovecs we've already written completely */ for (i = 0; i < args.iovcnt; i++, new_iov++) { if (w == 0) break; /* * partially written iov, * modify and retry with current iovec in front */ if (new_iov->iov_len > (size_t)w) { VALUE base = (VALUE)new_iov->iov_base; new_iov->iov_len -= w; new_iov->iov_base = (void *)(base + w); break; } w -= new_iov->iov_len; } /* retry without the already-written iovecs */ args.iovcnt -= i; args.iov = new_iov; } } return LONG2NUM(result); }
.command = CSTRING(sql), .n_args = RARRAY_LEN(bind), .data = bind_args_data, .size = bind_args_size, .format = bind_args_fmt }; result = (PGresult *)rb_thread_blocking_region(nogvl_pq_exec_params, &q, RUBY_UBF_IO, 0); rb_gc_unregister_address(&bind); free(bind_args_size); free(bind_args_data); free(bind_args_fmt); } else { Query q = {.connection = a->connection, .command = CSTRING(sql)}; result = (PGresult *)rb_thread_blocking_region(nogvl_pq_exec, &q, RUBY_UBF_IO, 0); } db_postgres_check_result(result); return db_postgres_result_load(db_postgres_result_allocate(cDPR), result); } VALUE db_postgres_adapter_begin(int argc, VALUE *argv, VALUE self) { char command[256]; VALUE savepoint; PGresult *result; Adapter *a = db_postgres_adapter_handle_safe(self); rb_scan_args(argc, argv, "01", &savepoint); if (a->t_nesting == 0) {
// There is really no way to know we're unlocked. So just make sure the arguments // go through fine. static VALUE thread_spec_rb_thread_blocking_region() { VALUE ret = rb_thread_blocking_region(do_unlocked, (void*)1, 0, 0); if(ret == (VALUE)1) return Qtrue; return Qfalse; }
static VALUE rb_monitor_run_bang(VALUE self) { BOOL already_running, waiting_succeeded; WDM_PMonitor monitor; WDM_DEBUG("Running the monitor!"); Data_Get_Struct(self, WDM_Monitor, monitor); already_running = FALSE; EnterCriticalSection(&monitor->lock); if ( monitor->running ) { already_running = TRUE; } else { monitor->running = TRUE; } LeaveCriticalSection(&monitor->lock); if (already_running) { WDM_DEBUG("Not doing anything because the monitor is already running!"); return Qnil; } // Reset events ResetEvent(monitor->process_event); ResetEvent(monitor->stop_event); monitor->monitoring_thread = CreateThread( NULL, // default security attributes 0, // use default stack size start_monitoring, // thread function name monitor, // argument to thread function 0, // use default creation flags NULL // Ignore thread identifier ); if ( monitor->monitoring_thread == NULL ) { rb_raise(eWDM_Error, "Can't create a thread for the monitor!"); } while ( monitor->running ) { // Ruby 2.2 removed the 'rb_thread_blocking_region' function. Hence, we now need // to check if the replacement function is defined and use it if it's available. #ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL waiting_succeeded = rb_thread_call_without_gvl(wait_for_changes, monitor->process_event, stop_monitoring, monitor); #else waiting_succeeded = rb_thread_blocking_region(wait_for_changes, monitor->process_event, stop_monitoring, monitor); #endif if ( waiting_succeeded == Qfalse ) { rb_raise(eWDM_Error, "Failed while waiting for a change in the watched directories!"); } if ( ! monitor->running ) { wdm_queue_empty(monitor->changes); return Qnil; } process_changes(monitor->changes); if ( ! ResetEvent(monitor->process_event) ) { rb_raise(eWDM_Error, "Couldn't reset system events to watch for changes!"); } } return Qnil; }
static void Coolio_Loop_ev_loop_oneshot(struct Coolio_Loop *loop_data) { /* Use Ruby 1.9's rb_thread_blocking_region call to make a blocking system call */ rb_thread_blocking_region(Coolio_Loop_ev_loop_oneshot_blocking, loop_data, RUBY_UBF_IO, 0); }
/* * call-seq: * multi = Curl::Multi.new * easy1 = Curl::Easy.new('url') * easy2 = Curl::Easy.new('url') * * multi.add(easy1) * multi.add(easy2) * * multi.perform do * # while idle other code my execute here * end * * Run multi handles, looping selecting when data can be transfered */ VALUE ruby_curl_multi_perform(int argc, VALUE *argv, VALUE self) { CURLMcode mcode; ruby_curl_multi *rbcm; int maxfd, rc; fd_set fdread, fdwrite, fdexcep; #ifdef _WIN32 fd_set crt_fdread, crt_fdwrite, crt_fdexcep; #endif long timeout_milliseconds; struct timeval tv = {0, 0}; VALUE block = Qnil; #ifdef HAVE_RB_THREAD_BLOCKING_REGION struct _select_set fdset_args; #endif rb_scan_args(argc, argv, "0&", &block); Data_Get_Struct(self, ruby_curl_multi, rbcm); timeout_milliseconds = cCurlMutiDefaulttimeout; rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) ); rb_curl_multi_read_info( self, rbcm->handle ); if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); } do { while (rbcm->running) { #ifdef HAVE_CURL_MULTI_TIMEOUT /* get the curl suggested time out */ mcode = curl_multi_timeout(rbcm->handle, &timeout_milliseconds); if (mcode != CURLM_OK) { raise_curl_multi_error_exception(mcode); } #else /* libcurl doesn't have a timeout method defined, initialize to -1 we'll pick up the default later */ timeout_milliseconds = -1; #endif if (timeout_milliseconds == 0) { /* no delay */ rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) ); rb_curl_multi_read_info( self, rbcm->handle ); if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); } continue; } if (timeout_milliseconds < 0 || timeout_milliseconds > cCurlMutiDefaulttimeout) { timeout_milliseconds = cCurlMutiDefaulttimeout; /* libcurl doesn't know how long to wait, use a default timeout */ /* or buggy versions libcurl sometimes reports huge timeouts... let's cap it */ } tv.tv_sec = 0; /* never wait longer than 1 second */ tv.tv_usec = (int)(timeout_milliseconds * 1000); /* XXX: int is the right type for OSX, what about linux? */ FD_ZERO(&fdread); FD_ZERO(&fdwrite); FD_ZERO(&fdexcep); /* load the fd sets from the multi handle */ mcode = curl_multi_fdset(rbcm->handle, &fdread, &fdwrite, &fdexcep, &maxfd); if (mcode != CURLM_OK) { raise_curl_multi_error_exception(mcode); } #ifdef _WIN32 create_crt_fd(&fdread, &crt_fdread); create_crt_fd(&fdwrite, &crt_fdwrite); create_crt_fd(&fdexcep, &crt_fdexcep); #endif #ifdef HAVE_RB_THREAD_BLOCKING_REGION fdset_args.maxfd = maxfd+1; fdset_args.fdread = &fdread; fdset_args.fdwrite = &fdwrite; fdset_args.fdexcep = &fdexcep; fdset_args.tv = &tv; rc = rb_thread_blocking_region(curb_select, &fdset_args, RUBY_UBF_IO, 0); #else rc = rb_thread_select(maxfd+1, &fdread, &fdwrite, &fdexcep, &tv); #endif #ifdef _WIN32 cleanup_crt_fd(&fdread, &crt_fdread); cleanup_crt_fd(&fdwrite, &crt_fdwrite); cleanup_crt_fd(&fdexcep, &crt_fdexcep); #endif switch(rc) { case -1: if(errno != EINTR) { rb_raise(rb_eRuntimeError, "select(): %s", strerror(errno)); break; } case 0: /* timeout */ default: /* action */ rb_curl_multi_run( self, rbcm->handle, &(rbcm->running) ); rb_curl_multi_read_info( self, rbcm->handle ); if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); } break; } } } while( rbcm->running ); rb_curl_multi_read_info( self, rbcm->handle ); if (block != Qnil) { rb_funcall(block, rb_intern("call"), 1, self); } return Qtrue; }