From e72d5103e392c0196607a3c217a623aed2031046 Mon Sep 17 00:00:00 2001 From: John Keiser Date: Tue, 25 Mar 2014 20:45:18 -0700 Subject: [PATCH] Get new GVL code working on 1.8.7 --- ext/lxc/extconf.rb | 7 + ext/lxc/lxc.c | 168 ++++++----- test/test_lxc_create_asynchronous.rb | 34 +-- test/test_lxc_long_running.rb | 412 ++++++++++++++------------- 4 files changed, 325 insertions(+), 296 deletions(-) diff --git a/ext/lxc/extconf.rb b/ext/lxc/extconf.rb index 07fd4d4..5fb4f9a 100644 --- a/ext/lxc/extconf.rb +++ b/ext/lxc/extconf.rb @@ -3,5 +3,12 @@ require 'mkmf' abort 'missing liblxc' unless find_library('lxc', 'lxc_container_new') abort 'missing lxc/lxccontainer.h' unless have_header('lxc/lxccontainer.h') +def add_define(name) + $defs.push("-D#{name}") +end + +add_define "HAVE_RB_THREAD_CALL_WITHOUT_GVL" if have_func('rb_thread_call_without_gvl') +add_define "HAVE_RB_THREAD_BLOCKING_REGION" if have_func('rb_thread_blocking_region') + $CFLAGS += " -Wall #{ENV['CFLAGS']}" create_makefile('lxc/lxc') diff --git a/ext/lxc/lxc.c b/ext/lxc/lxc.c index a5143ed..136cd2d 100644 --- a/ext/lxc/lxc.c +++ b/ext/lxc/lxc.c @@ -9,9 +9,29 @@ #define SYMBOL(s) ID2SYM(rb_intern(s)) +#if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) // Defined in Ruby, but not in all Ruby versions' header files extern void *rb_thread_call_without_gvl(void *(*func)(void *), void *data1, - rb_unblock_function_t *ubf, void *data2); + rb_unblock_function_t *ubf, void *data2); +#define WITHOUT_GVL_RESULT_TYPE void * +#define WITHOUT_GVL_RESULT(__X__) (void *)(intptr_t)(__X__) +#define WITHOUT_GVL_CALL(__FUNC__, __ARG__) (int)(intptr_t)rb_thread_call_without_gvl(__FUNC__, __ARG__, NULL, NULL) +#define WITHOUT_GVL_CALL_NOOP(__FUNC__, __ARG__) rb_thread_call_without_gvl(__FUNC__, __ARG__, NULL, NULL) +#define WITHOUT_GVL_CALL2(__FUNC__, __ARG__, __KILLFUNC__, __KILLARG__) (int)(intptr_t)rb_thread_call_without_gvl(__FUNC__, __ARG__, __KILLFUNC__, __KILLARG__) +#elif defined(HAVE_RB_THREAD_BLOCKING_REGION) +#define WITHOUT_GVL_RESULT_TYPE VALUE +#define WITHOUT_GVL_RESULT(__X__) INT2NUM(__X__) +#define WITHOUT_GVL_CALL(__FUNC__, __ARG__) NUM2INT(rb_thread_blocking_region(__FUNC__, __ARG__, NULL, NULL)) +#define WITHOUT_GVL_CALL_NOOP(__FUNC__, __ARG__) rb_thread_blocking_region(__FUNC__, __ARG__, NULL, NULL) +#define WITHOUT_GVL_CALL2(__FUNC__, __ARG__, __KILLFUNC__, __KILLARG__) NUM2INT(rb_thread_blocking_region(__FUNC__, __ARG__, __KILLFUNC__, __KILLARG__)) +#else +#define WITHOUT_GVL_RESULT_TYPE int +#define WITHOUT_GVL_RESULT(__X__) __X__ +#define WITHOUT_GVL_CALL(__FUNC__, __ARG__) __FUNC__(__ARG__) +#define WITHOUT_GVL_CALL_NOOP(__FUNC__, __ARG__) __FUNC__(__ARG__) +#define WITHOUT_GVL_CALL2(__FUNC__, __ARG__, __KILLFUNC__, __KILLARG__) __FUNC__(__ARG__) +#endif + extern int lxc_wait_for_pid_status(pid_t pid); extern long lxc_config_parse_arch(const char *arch); @@ -173,7 +193,7 @@ struct list_containers_without_gvl_args char **names; }; -static void * +static WITHOUT_GVL_RESULT_TYPE list_containers_without_gvl(void *args_void) { struct list_containers_without_gvl_args *args = (struct list_containers_without_gvl_args *)args_void; @@ -185,7 +205,7 @@ list_containers_without_gvl(void *args_void) num_containers = list_active_containers(args->config, &args->names, NULL); else if (args->defined) num_containers = list_defined_containers(args->config, &args->names, NULL); - return (void *)(intptr_t)num_containers; + return WITHOUT_GVL_RESULT(num_containers); } /* @@ -228,7 +248,7 @@ lxc_list_containers(int argc, VALUE *argv, VALUE self) if (!NIL_P(rb_config)) args.config = StringValuePtr(rb_config); } - num_containers = (int)(intptr_t)rb_thread_call_without_gvl(list_containers_without_gvl, &args, NULL, NULL); + num_containers = WITHOUT_GVL_CALL(list_containers_without_gvl, &args); if (num_containers < 0) rb_raise(Error, "failure to list containers"); @@ -414,11 +434,11 @@ struct add_device_node_without_gvl_args char *dest_path; }; -static void * +static WITHOUT_GVL_RESULT_TYPE add_device_node_without_gvl(void *args_void) { struct add_device_node_without_gvl_args *args = (struct add_device_node_without_gvl_args *)args_void; - return (void*)args->data->container->add_device_node(args->data->container, args->src_path, args->dest_path); + return WITHOUT_GVL_RESULT(args->data->container->add_device_node(args->data->container, args->src_path, args->dest_path)); } /* @@ -440,7 +460,7 @@ container_add_device_node(int argc, VALUE *argv, VALUE self) Data_Get_Struct(self, struct container_data, args.data); - ret = (int)(intptr_t)rb_thread_call_without_gvl(add_device_node_without_gvl, &args, NULL, NULL); + ret = WITHOUT_GVL_CALL(add_device_node_without_gvl, &args); if (!ret) rb_raise(Error, "unable to add device node"); @@ -630,17 +650,19 @@ err: return NULL; } -static void * +static WITHOUT_GVL_RESULT_TYPE lxc_wait_for_pid_status_without_gvl(void *pid) { - return (void *)(intptr_t)lxc_wait_for_pid_status(*(pid_t*)pid); + return WITHOUT_GVL_RESULT(lxc_wait_for_pid_status(*(pid_t*)pid)); } +#if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) || defined(HAVE_RB_THREAD_BLOCKING_REGION) static void kill_pid_without_gvl(void *pid) { kill(*(pid_t *)pid, SIGKILL); } +#endif /* * call-seq: @@ -699,13 +721,8 @@ container_attach(int argc, VALUE *argv, VALUE self) goto out; if (wait) { - ret = (int)(intptr_t) - rb_thread_call_without_gvl( - lxc_wait_for_pid_status_without_gvl, - &pid, - kill_pid_without_gvl, - &pid - ); + ret = WITHOUT_GVL_CALL2(lxc_wait_for_pid_status_without_gvl, &pid, + kill_pid_without_gvl, &pid); /* handle case where attach fails */ if (WIFEXITED(ret) && WEXITSTATUS(ret) == 255) ret = -1; @@ -770,7 +787,7 @@ struct clone_without_gvl_args char **hook_args; }; -static void * +static WITHOUT_GVL_RESULT_TYPE clone_without_gvl(void *args_void) { struct clone_without_gvl_args *args = (struct clone_without_gvl_args *)args_void; @@ -778,7 +795,7 @@ clone_without_gvl(void *args_void) args->new_container = container->clone(container, args->name, args->config_path, args->flags, args->bdev_type, args->bdev_data, args->new_size, args->hook_args); - return NULL; + return WITHOUT_GVL_RESULT(0); } /* @@ -846,7 +863,7 @@ container_clone(int argc, VALUE *argv, VALUE self) Data_Get_Struct(self, struct container_data, args.data); - rb_thread_call_without_gvl(clone_without_gvl, &args, NULL, NULL); + WITHOUT_GVL_CALL_NOOP(clone_without_gvl, &args); if (args.hook_args) free_c_string_array(args.hook_args); @@ -871,13 +888,14 @@ struct console_without_gvl_args int escape; }; -static void * +static WITHOUT_GVL_RESULT_TYPE console_without_gvl(void *args_void) { struct console_without_gvl_args *args = (struct console_without_gvl_args *)args_void; - return (void*)(intptr_t)args->data->container->console(args->data->container, args->tty_num, + struct lxc_container *container = args->data->container; + return WITHOUT_GVL_RESULT(container->console(container, args->tty_num, args->stdin_fd, args->stdout_fd, - args->stderr_fd, args->escape); + args->stderr_fd, args->escape)); } /* @@ -904,7 +922,7 @@ container_console(int argc, VALUE *argv, VALUE self) args.tty_num = -1; args.stdin_fd = 0; args.stdout_fd = 1; - args.stdout_fd = 2; + args.stderr_fd = 2; args.escape = 1; rb_scan_args(argc, argv, "01", &rb_opts); @@ -934,7 +952,7 @@ container_console(int argc, VALUE *argv, VALUE self) Data_Get_Struct(self, struct container_data, args.data); - ret = (int)(intptr_t)rb_thread_call_without_gvl(console_without_gvl, &args, NULL, NULL); + ret = WITHOUT_GVL_CALL(console_without_gvl, &args); if (ret != 0) rb_raise(Error, "unable to access container console"); @@ -977,11 +995,11 @@ struct container_create_without_gvl_args { char **args; }; -static void * +static WITHOUT_GVL_RESULT_TYPE container_create_without_gvl(void *args_void) { struct container_create_without_gvl_args *args = (struct container_create_without_gvl_args *)args_void; - return (void*)args->data->container->create(args->data->container, args->template, args->bdevtype, NULL, args->flags, args->args); + return WITHOUT_GVL_RESULT(args->data->container->create(args->data->container, args->template, args->bdevtype, NULL, args->flags, args->args)); } /* @@ -1013,7 +1031,7 @@ container_create(int argc, VALUE *argv, VALUE self) Data_Get_Struct(self, struct container_data, args.data); - ret = (int)(intptr_t)rb_thread_call_without_gvl(container_create_without_gvl, &args, NULL, NULL); + ret = WITHOUT_GVL_CALL(container_create_without_gvl, &args); if (!NIL_P(rb_args)) free_c_string_array(args.args); @@ -1025,11 +1043,11 @@ container_create(int argc, VALUE *argv, VALUE self) } -static void * +static WITHOUT_GVL_RESULT_TYPE destroy_without_gvl(void *data_void) { struct container_data *data = (struct container_data *)data_void; - return (void *)(intptr_t)data->container->destroy(data->container); + return WITHOUT_GVL_RESULT(data->container->destroy(data->container)); } /* @@ -1046,17 +1064,17 @@ container_destroy(VALUE self) Data_Get_Struct(self, struct container_data, data); - ret = (int)(intptr_t)rb_thread_call_without_gvl(destroy_without_gvl, data, NULL, NULL); + ret = WITHOUT_GVL_CALL(destroy_without_gvl, data); if (!ret) rb_raise(Error, "unable to destroy container"); return self; } -static void * +static WITHOUT_GVL_RESULT_TYPE freeze_without_gvl(void *data_void) { struct container_data *data = (struct container_data *)data_void; - return (void *)(intptr_t)data->container->freeze(data->container); + return WITHOUT_GVL_RESULT(data->container->freeze(data->container)); } /* @@ -1073,7 +1091,7 @@ container_freeze(VALUE self) Data_Get_Struct(self, struct container_data, data); - ret = (int)(intptr_t)rb_thread_call_without_gvl(freeze_without_gvl, data, NULL, NULL); + ret = WITHOUT_GVL_CALL(freeze_without_gvl, data); if (!ret) rb_raise(Error, "unable to freeze container"); @@ -1286,11 +1304,11 @@ struct load_config_without_gvl_args char *path; }; -static void * +static WITHOUT_GVL_RESULT_TYPE load_config_without_gvl(void *args_void) { struct load_config_without_gvl_args *args = (struct load_config_without_gvl_args *)args_void; - return (void *)(intptr_t)args->data->container->load_config(args->data->container, args->path); + return WITHOUT_GVL_RESULT(args->data->container->load_config(args->data->container, args->path)); } /* @@ -1311,18 +1329,18 @@ container_load_config(int argc, VALUE *argv, VALUE self) Data_Get_Struct(self, struct container_data, args.data); - ret = (int)(intptr_t)rb_thread_call_without_gvl(load_config_without_gvl, &args, NULL, NULL); + ret = WITHOUT_GVL_CALL(load_config_without_gvl, &args); if (!ret) rb_raise(Error, "unable to load configuration file"); return self; } -static void * +static WITHOUT_GVL_RESULT_TYPE reboot_without_gvl(void* data_void) { struct container_data *data = (struct container_data *)data_void; - return (void *)(intptr_t)data->container->reboot(data->container); + return WITHOUT_GVL_RESULT(data->container->reboot(data->container)); } /* @@ -1339,7 +1357,7 @@ container_reboot(VALUE self) Data_Get_Struct(self, struct container_data, data); - ret = (int)(intptr_t)rb_thread_call_without_gvl(reboot_without_gvl, data, NULL, NULL); + ret = WITHOUT_GVL_CALL(reboot_without_gvl, data); if (!ret) rb_raise(Error, "unable to reboot container"); @@ -1353,11 +1371,11 @@ struct remove_device_node_without_gvl_args char *dest_path; }; -static void * +static WITHOUT_GVL_RESULT_TYPE remove_device_node_without_gvl(void *args_void) { struct remove_device_node_without_gvl_args *args = (struct remove_device_node_without_gvl_args *)args_void; - return (void *)(intptr_t)args->data->container->remove_device_node(args->data->container, args->src_path, args->dest_path); + return WITHOUT_GVL_RESULT(args->data->container->remove_device_node(args->data->container, args->src_path, args->dest_path)); } /* @@ -1379,7 +1397,7 @@ container_remove_device_node(int argc, VALUE *argv, VALUE self) Data_Get_Struct(self, struct container_data, args.data); - ret = (int)(intptr_t)rb_thread_call_without_gvl(remove_device_node_without_gvl, &args, NULL, NULL); + ret = WITHOUT_GVL_CALL(remove_device_node_without_gvl, &args); if (!ret) rb_raise(Error, "unable to remove device node"); @@ -1392,11 +1410,11 @@ struct rename_without_gvl_args char *name; }; -static void * +static WITHOUT_GVL_RESULT_TYPE rename_without_gvl(void *args_void) { struct rename_without_gvl_args *args = (struct rename_without_gvl_args *)args_void; - return (void *)(intptr_t)args->data->container->rename(args->data->container, args->name); + return WITHOUT_GVL_RESULT(args->data->container->rename(args->data->container, args->name)); } /* @@ -1417,7 +1435,7 @@ container_rename(VALUE self, VALUE rb_name) args.name = StringValuePtr(rb_name); - ret = (int)(intptr_t)rb_thread_call_without_gvl(rename_without_gvl, &args, NULL, NULL); + ret = WITHOUT_GVL_CALL(rename_without_gvl, &args); if (!ret) rb_raise(Error, "unable to rename container"); @@ -1461,11 +1479,11 @@ struct save_config_without_gvl_args char *path; }; -static void * +static WITHOUT_GVL_RESULT_TYPE save_config_without_gvl(void *args_void) { struct save_config_without_gvl_args *args = (struct save_config_without_gvl_args *)args_void; - return (void *)(intptr_t)args->data->container->save_config(args->data->container, args->path); + return WITHOUT_GVL_RESULT(args->data->container->save_config(args->data->container, args->path)); } static VALUE @@ -1480,7 +1498,7 @@ container_save_config(int argc, VALUE *argv, VALUE self) Data_Get_Struct(self, struct container_data, args.data); - ret = (int)(intptr_t)rb_thread_call_without_gvl(save_config_without_gvl, &args, NULL, NULL); + ret = WITHOUT_GVL_CALL(save_config_without_gvl, &args); if (!ret) rb_raise(Error, "unable to save configuration file"); @@ -1586,11 +1604,11 @@ struct shutdown_without_gvl_args int timeout; }; -static void * +static WITHOUT_GVL_RESULT_TYPE shutdown_without_gvl(void* args_void) { struct shutdown_without_gvl_args *args = (struct shutdown_without_gvl_args *)args_void; - return (void *)(intptr_t)args->data->container->shutdown(args->data->container, args->timeout); + return WITHOUT_GVL_RESULT(args->data->container->shutdown(args->data->container, args->timeout)); } /* @@ -1614,7 +1632,7 @@ container_shutdown(int argc, VALUE *argv, VALUE self) args.timeout = NIL_P(rb_timeout) ? -1 : NUM2INT(rb_timeout); - ret = (int)(intptr_t)rb_thread_call_without_gvl(shutdown_without_gvl, &args, NULL, NULL); + ret = WITHOUT_GVL_CALL(shutdown_without_gvl, &args); if (!ret) rb_raise(Error, "unable to shutdown container"); @@ -1627,11 +1645,11 @@ struct snapshot_without_gvl_args char *path; }; -static void * +static WITHOUT_GVL_RESULT_TYPE snapshot_without_gvl(void* args_void) { struct snapshot_without_gvl_args *args = (struct snapshot_without_gvl_args *)args_void; - return (void *)(intptr_t)args->data->container->snapshot(args->data->container, args->path); + return WITHOUT_GVL_RESULT(args->data->container->snapshot(args->data->container, args->path)); } /* @@ -1653,7 +1671,7 @@ container_snapshot(int argc, VALUE *argv, VALUE self) Data_Get_Struct(self, struct container_data, args.data); - ret = (int)(intptr_t)rb_thread_call_without_gvl(snapshot_without_gvl, &args, NULL, NULL); + ret = WITHOUT_GVL_CALL(snapshot_without_gvl, &args); if (ret < 0) rb_raise(Error, "unable to snapshot container"); @@ -1670,11 +1688,11 @@ struct snapshot_destroy_without_gvl_args char *name; }; -static void * +static WITHOUT_GVL_RESULT_TYPE snapshot_destroy_without_gvl(void *args_void) { struct snapshot_destroy_without_gvl_args *args = (struct snapshot_destroy_without_gvl_args *)args_void; - return (void *)(intptr_t)args->data->container->snapshot_destroy(args->data->container, args->name); + return WITHOUT_GVL_RESULT(args->data->container->snapshot_destroy(args->data->container, args->name)); } /* @@ -1693,7 +1711,7 @@ container_snapshot_destroy(VALUE self, VALUE rb_name) args.name = StringValuePtr(rb_name); - ret = (int)(intptr_t)rb_thread_call_without_gvl(snapshot_destroy_without_gvl, &args, NULL, NULL); + ret = WITHOUT_GVL_CALL(snapshot_destroy_without_gvl, &args); if (!ret) rb_raise(Error, "unable to destroy snapshot"); @@ -1706,11 +1724,11 @@ struct snapshot_list_without_gvl_args struct lxc_snapshot *snapshots; }; -static void * +static WITHOUT_GVL_RESULT_TYPE snapshot_list_without_gvl(void *args_void) { struct snapshot_list_without_gvl_args *args = (struct snapshot_list_without_gvl_args *)args_void; - return (void *)(intptr_t)args->data->container->snapshot_list(args->data->container, &args->snapshots); + return WITHOUT_GVL_RESULT(args->data->container->snapshot_list(args->data->container, &args->snapshots)); } /* @@ -1728,7 +1746,7 @@ container_snapshot_list(VALUE self) Data_Get_Struct(self, struct container_data, args.data); - num_snapshots = (int)(intptr_t)rb_thread_call_without_gvl(snapshot_list_without_gvl, &args, NULL, NULL); + num_snapshots = WITHOUT_GVL_CALL(snapshot_list_without_gvl, &args); if (num_snapshots < 0) rb_raise(Error, "unable to list snapshots"); @@ -1753,11 +1771,11 @@ struct snapshot_restore_without_gvl_args char *new_name; }; -static void * +static WITHOUT_GVL_RESULT_TYPE snapshot_restore_without_gvl(void *args_void) { struct snapshot_restore_without_gvl_args *args = (struct snapshot_restore_without_gvl_args *)args_void; - return (void *)(intptr_t)args->data->container->snapshot_restore(args->data->container, args->name, args->new_name); + return WITHOUT_GVL_RESULT(args->data->container->snapshot_restore(args->data->container, args->name, args->new_name)); } /* @@ -1779,7 +1797,7 @@ container_snapshot_restore(int argc, VALUE *argv, VALUE self) Data_Get_Struct(self, struct container_data, args.data); - ret = (int)(intptr_t)rb_thread_call_without_gvl(snapshot_restore_without_gvl, &args, NULL, NULL); + ret = WITHOUT_GVL_CALL(snapshot_restore_without_gvl, &args); if (!ret) rb_raise(Error, "unable to restore snapshot"); @@ -1796,14 +1814,14 @@ struct start_without_gvl_args char **args; }; -static void * +static WITHOUT_GVL_RESULT_TYPE start_without_gvl(void *args_void) { struct start_without_gvl_args *args = (struct start_without_gvl_args *)args_void; struct lxc_container *container = args->data->container; container->want_close_all_fds(container, args->close_fds); container->want_daemonize(container, args->daemonize); - return (void *)(intptr_t)container->start(container, args->use_init, args->args); + return WITHOUT_GVL_RESULT(container->start(container, args->use_init, args->args)); } /* @@ -1848,7 +1866,7 @@ container_start(int argc, VALUE *argv, VALUE self) Data_Get_Struct(self, struct container_data, args.data); - ret = (int)(intptr_t)rb_thread_call_without_gvl(start_without_gvl, &args, NULL, NULL); + ret = WITHOUT_GVL_CALL(start_without_gvl, &args); if (!NIL_P(rb_args)) free_c_string_array(args.args); @@ -1859,11 +1877,11 @@ container_start(int argc, VALUE *argv, VALUE self) return self; } -static void * +static WITHOUT_GVL_RESULT_TYPE stop_without_gvl(void *data_void) { struct container_data *data = (struct container_data *)data_void; - return (void *)(intptr_t)data->container->stop(data->container); + return WITHOUT_GVL_RESULT(data->container->stop(data->container)); } /* @@ -1880,18 +1898,18 @@ container_stop(VALUE self) Data_Get_Struct(self, struct container_data, data); - ret = (int)(intptr_t)rb_thread_call_without_gvl(stop_without_gvl, data, NULL, NULL); + ret = WITHOUT_GVL_CALL(stop_without_gvl, data); if (!ret) rb_raise(Error, "unable to stop container"); return self; } -static void * +static WITHOUT_GVL_RESULT_TYPE unfreeze_without_gvl(void *data_void) { struct container_data *data = (struct container_data *)data_void; - return (void *)(intptr_t)data->container->unfreeze(data->container); + return WITHOUT_GVL_RESULT(data->container->unfreeze(data->container)); } /* @@ -1908,7 +1926,7 @@ container_unfreeze(VALUE self) Data_Get_Struct(self, struct container_data, data); - ret = (int)(intptr_t)rb_thread_call_without_gvl(unfreeze_without_gvl, data, NULL, NULL); + ret = WITHOUT_GVL_CALL(unfreeze_without_gvl, data); if (!ret) rb_raise(Error, "unable to unfreeze container: #{lxc_strerror(ret)}"); @@ -1922,11 +1940,11 @@ struct wait_without_gvl_args char *state; }; -static void * +static WITHOUT_GVL_RESULT_TYPE wait_without_gvl(void *args_void) { struct wait_without_gvl_args *args = (struct wait_without_gvl_args *)args_void; - return (void *)(intptr_t)args->data->container->wait(args->data->container, args->state, args->timeout); + return WITHOUT_GVL_RESULT(args->data->container->wait(args->data->container, args->state, args->timeout)); } /* @@ -1953,7 +1971,7 @@ container_wait(int argc, VALUE *argv, VALUE self) Data_Get_Struct(self, struct container_data, args.data); - ret = (int)(intptr_t)rb_thread_call_without_gvl(wait_without_gvl, &args, NULL, NULL); + ret = WITHOUT_GVL_CALL(wait_without_gvl, &args); if (!ret) rb_raise(Error, "error waiting for container"); diff --git a/test/test_lxc_create_asynchronous.rb b/test/test_lxc_create_asynchronous.rb index aacefca..494a022 100644 --- a/test/test_lxc_create_asynchronous.rb +++ b/test/test_lxc_create_asynchronous.rb @@ -1,25 +1,27 @@ $:.unshift File.expand_path(File.join(File.dirname(__FILE__), 'lib')) -require 'test/unit' -require 'lxc' -require 'test_helpers' +if RUBY_VERSION.to_f > 1.8 + require 'test/unit' + require 'lxc' + require 'test_helpers' -class TestLXCCreateAsynchronous < Test::Unit::TestCase - include TestHelpers + class TestLXCCreateAsynchronous < Test::Unit::TestCase + include TestHelpers - def setup - if Process::Sys::geteuid != 0 - raise 'This test must be ran as root' + def setup + if Process::Sys::geteuid != 0 + raise 'This test must be ran as root' + end + @name = 'test_async_create' + container = LXC::Container.new(@name) + container.destroy if container.defined? end - @name = 'test_async_create' - container = LXC::Container.new(@name) - container.destroy if container.defined? - end - def test_create_allows_ruby_to_continue - c = LXC::Container.new(@name) - assert_long_running_function_does_not_block_ruby do - c.create('ubuntu') + def test_create_allows_ruby_to_continue + c = LXC::Container.new(@name) + assert_long_running_function_does_not_block_ruby do + c.create('ubuntu') + end end end end diff --git a/test/test_lxc_long_running.rb b/test/test_lxc_long_running.rb index 74c777d..adffeb4 100644 --- a/test/test_lxc_long_running.rb +++ b/test/test_lxc_long_running.rb @@ -4,225 +4,227 @@ require 'test/unit' require 'lxc' require 'test_helpers' -class TestLXCLongRunning < Test::Unit::TestCase - include TestHelpers +if RUBY_VERSION.to_f > 1.8 + class TestLXCLongRunning < Test::Unit::TestCase + include TestHelpers - def setup - if Process::Sys::geteuid != 0 - raise 'This test must be run as root' - end - @name = 'test' - @container = LXC::Container.new(@name) - @container.create('ubuntu') unless @container.defined? - @container.start unless @container.running? - @container.unfreeze - # Destroy leftover snapshots so we don't take up the whole disk - @container.snapshot_list.each do |snapshot, commentfile, timestamp, snapshotfile| - @container.snapshot_destroy(snapshot) - end - end - -# TODO find a way to test unblockness for functions that don't run quite so long -# def test_add_device_node_allows_ruby_to_continue -# assert_long_running_function_does_not_block_ruby do -# @container.add_device_node('/dev/ttyS0') -# end -# end - - def test_attach_wait_true_allows_ruby_to_continue - assert_long_running_function_does_not_block_ruby do - @container.attach(wait: true) do - sleep(2) + def setup + if Process::Sys::geteuid != 0 + raise 'This test must be run as root' end - end - end - - def test_clone_allows_ruby_to_continue - # Ensure the "cloned" VM does not exist - cloned_name = "#{@name}_cloned" - cloned_to_destroy = LXC::Container.new(cloned_name) - cloned_to_destroy.destroy if cloned_to_destroy.defined? - - cloned = nil - @container.stop - begin - assert_long_running_function_does_not_block_ruby do - cloned = @container.clone(cloned_name) - end - ensure - @container.start - end - # Verify that it did something - assert(@container.name == @name, "original container name has changed from '#{@name}' to '#{@container.name}''") - assert(@container.running?, "original container is not running") - assert(cloned.name == cloned_name, "new container name should be '#{cloned_name}', is '#{cloned.name}'") - assert(cloned.init_pid != @container.init_pid, "cloned container init pid is the same as the original! Something ain't right.") - end - -# TODO Unsure how to test this without attaching to the actual tty! Must be a way to obtain a pipe or false tty of some sort. Manual run reveals it works, however. -# def test_console_allows_ruby_to_continue -# assert_long_running_function_does_not_block_ruby do -# @container.console -# end -# end - - def test_destroy_allows_ruby_to_continue - container = LXC::Container.new("test_destroy") - container.create('ubuntu') if !container.defined? - assert_long_running_function_does_not_block_ruby do - container.destroy - end - assert(!container.defined?, "Destroy did not destroy the container!") - end - -# TODO find a way to test unblockness for functions that don't run quite so long -# def test_freeze_allows_ruby_to_continue -# begin -# assert_long_running_function_does_not_block_ruby do -# @container.freeze -# end -# ensure -# @container.unfreeze -# end -# end - -# TODO find a way to test unblockness for functions that don't run quite so long -# def test_load_config_allows_ruby_to_continue -# File.open('/tmp/blah', 'w') do |file| -# file.write("\n\n\n\n") -# end -# begin -# container = LXC::Container.new('blahdeblah') -# assert_long_running_function_does_not_block_ruby do -# container.load_config('/tmp/blah') -# end -# ensure -# File.delete('/tmp/blah') -# end -# end - -# TODO find a way to test unblockness for functions that don't run quite so long -# def test_reboot_allows_ruby_to_continue -# assert_long_running_function_does_not_block_ruby do -# @container.reboot -# end -# end - -# TODO find a way to test unblockness for functions that don't run quite so long -# def test_remove_device_node_allows_ruby_to_continue -# assert_long_running_function_does_not_block_ruby do -# @container.remove_device_node('/dev/ttyS0') -# end -# end - -# TODO find a way to test unblockness for functions that don't run quite so long -# def test_save_config_allows_ruby_to_continue -# File.unlink('/tmp/blah') if File.exist?('/tmp/blah') -# assert_long_running_function_does_not_block_ruby do -# @container.save_config('/tmp/blah') -# end -# assert(File.exist?('/tmp/blah'), "save_config did not save /tmp/blah!") -# end - - def test_shutdown_allows_ruby_to_continue - begin - assert_long_running_function_does_not_block_ruby do - @container.shutdown - end - ensure - @container.start if !@container.running? - end - end - - def test_snapshot_allows_ruby_to_continue - @container.stop - begin - assert_long_running_function_does_not_block_ruby do - @container.snapshot - end - ensure - @container.start - end - end - - def test_snapshot_destroy_allows_ruby_to_continue - @container.stop - begin - snapshot = @container.snapshot - assert_long_running_function_does_not_block_ruby do + @name = 'test' + @container = LXC::Container.new(@name) + @container.create('ubuntu') unless @container.defined? + @container.start unless @container.running? + @container.unfreeze + # Destroy leftover snapshots so we don't take up the whole disk + @container.snapshot_list.each do |snapshot, commentfile, timestamp, snapshotfile| @container.snapshot_destroy(snapshot) end - ensure - @container.start end - end -# TODO find a way to test unblockness for functions that don't run quite so long -# def test_snapshot_list_allows_ruby_to_continue -# @container.stop -# begin -# snapshot = @container.snapshot -# assert_long_running_function_does_not_block_ruby do -# assert(@container.snapshot_list > 0, "Snapshot list was empty!" -# end -# ensure -# @container.start -# end -# end + # TODO find a way to test unblockness for functions that don't run quite so long + # def test_add_device_node_allows_ruby_to_continue + # assert_long_running_function_does_not_block_ruby do + # @container.add_device_node('/dev/ttyS0') + # end + # end - def test_snapshot_restore_allows_ruby_to_continue - @container.stop - begin - snapshot = @container.snapshot + def test_attach_wait_true_allows_ruby_to_continue assert_long_running_function_does_not_block_ruby do - @container.snapshot_restore(snapshot) + @container.attach(:wait => true) do + sleep(2) + end end - ensure - @container.start end - end - def test_start_allows_ruby_to_continue - @container.stop - assert_long_running_function_does_not_block_ruby do - @container.start - end - assert(@container.running?, "Start did not start container! State = #{@container.state}") - end + def test_clone_allows_ruby_to_continue + # Ensure the "cloned" VM does not exist + cloned_name = "#{@name}_cloned" + cloned_to_destroy = LXC::Container.new(cloned_name) + cloned_to_destroy.destroy if cloned_to_destroy.defined? -# TODO find a way to test unblockness for functions that don't run quite so long -# def test_stop_allows_ruby_to_continue -# begin -# assert_long_running_function_does_not_block_ruby do -# @container.stop -# end -# assert(!@container.running?, "Stop did not stop container! State = #{@container.state}") -# ensure -# @container.start if !@container.running? -# end -# end - -# TODO find a way to test unblockness for functions that don't run quite so long -# def test_unfreeze_allows_ruby_to_continue -# @container.freeze -# assert_long_running_function_does_not_block_ruby do -# @container.unfreeze -# end -# end - - def test_wait_allows_ruby_to_continue - t = Thread.new do - sleep(0.5) + cloned = nil @container.stop - end - begin - assert_long_running_function_does_not_block_ruby do - @container.wait(:stopped, 2) + begin + assert_long_running_function_does_not_block_ruby do + cloned = @container.clone(cloned_name) + end + ensure + @container.start + end + # Verify that it did something + assert(@container.name == @name, "original container name has changed from '#{@name}' to '#{@container.name}''") + assert(@container.running?, "original container is not running") + assert(cloned.name == cloned_name, "new container name should be '#{cloned_name}', is '#{cloned.name}'") + assert(cloned.init_pid != @container.init_pid, "cloned container init pid is the same as the original! Something ain't right.") + end + + # TODO Unsure how to test this without attaching to the actual tty! Must be a way to obtain a pipe or false tty of some sort. Manual run reveals it works, however. + # def test_console_allows_ruby_to_continue + # assert_long_running_function_does_not_block_ruby do + # @container.console + # end + # end + + def test_destroy_allows_ruby_to_continue + container = LXC::Container.new("test_destroy") + container.create('ubuntu') if !container.defined? + assert_long_running_function_does_not_block_ruby do + container.destroy + end + assert(!container.defined?, "Destroy did not destroy the container!") + end + + # TODO find a way to test unblockness for functions that don't run quite so long + # def test_freeze_allows_ruby_to_continue + # begin + # assert_long_running_function_does_not_block_ruby do + # @container.freeze + # end + # ensure + # @container.unfreeze + # end + # end + + # TODO find a way to test unblockness for functions that don't run quite so long + # def test_load_config_allows_ruby_to_continue + # File.open('/tmp/blah', 'w') do |file| + # file.write("\n\n\n\n") + # end + # begin + # container = LXC::Container.new('blahdeblah') + # assert_long_running_function_does_not_block_ruby do + # container.load_config('/tmp/blah') + # end + # ensure + # File.delete('/tmp/blah') + # end + # end + + # TODO find a way to test unblockness for functions that don't run quite so long + # def test_reboot_allows_ruby_to_continue + # assert_long_running_function_does_not_block_ruby do + # @container.reboot + # end + # end + + # TODO find a way to test unblockness for functions that don't run quite so long + # def test_remove_device_node_allows_ruby_to_continue + # assert_long_running_function_does_not_block_ruby do + # @container.remove_device_node('/dev/ttyS0') + # end + # end + + # TODO find a way to test unblockness for functions that don't run quite so long + # def test_save_config_allows_ruby_to_continue + # File.unlink('/tmp/blah') if File.exist?('/tmp/blah') + # assert_long_running_function_does_not_block_ruby do + # @container.save_config('/tmp/blah') + # end + # assert(File.exist?('/tmp/blah'), "save_config did not save /tmp/blah!") + # end + + def test_shutdown_allows_ruby_to_continue + begin + assert_long_running_function_does_not_block_ruby do + @container.shutdown + end + ensure + @container.start if !@container.running? + end + end + + def test_snapshot_allows_ruby_to_continue + @container.stop + begin + assert_long_running_function_does_not_block_ruby do + @container.snapshot + end + ensure + @container.start + end + end + + def test_snapshot_destroy_allows_ruby_to_continue + @container.stop + begin + snapshot = @container.snapshot + assert_long_running_function_does_not_block_ruby do + @container.snapshot_destroy(snapshot) + end + ensure + @container.start + end + end + + # TODO find a way to test unblockness for functions that don't run quite so long + # def test_snapshot_list_allows_ruby_to_continue + # @container.stop + # begin + # snapshot = @container.snapshot + # assert_long_running_function_does_not_block_ruby do + # assert(@container.snapshot_list > 0, "Snapshot list was empty!" + # end + # ensure + # @container.start + # end + # end + + def test_snapshot_restore_allows_ruby_to_continue + @container.stop + begin + snapshot = @container.snapshot + assert_long_running_function_does_not_block_ruby do + @container.snapshot_restore(snapshot) + end + ensure + @container.start + end + end + + def test_start_allows_ruby_to_continue + @container.stop + assert_long_running_function_does_not_block_ruby do + @container.start + end + assert(@container.running?, "Start did not start container! State = #{@container.state}") + end + + # TODO find a way to test unblockness for functions that don't run quite so long + # def test_stop_allows_ruby_to_continue + # begin + # assert_long_running_function_does_not_block_ruby do + # @container.stop + # end + # assert(!@container.running?, "Stop did not stop container! State = #{@container.state}") + # ensure + # @container.start if !@container.running? + # end + # end + + # TODO find a way to test unblockness for functions that don't run quite so long + # def test_unfreeze_allows_ruby_to_continue + # @container.freeze + # assert_long_running_function_does_not_block_ruby do + # @container.unfreeze + # end + # end + + def test_wait_allows_ruby_to_continue + t = Thread.new do + sleep(0.5) + @container.stop + end + begin + assert_long_running_function_does_not_block_ruby do + @container.wait(:stopped, 2) + end + assert(@container.state == :stopped, "Container never stopped! State is now #{@container.state.inspect}") + ensure + t.join + @container.start if !@container.running? end - assert(@container.state == :stopped, "Container never stopped! State is now #{@container.state.inspect}") - ensure - t.join - @container.start if !@container.running? end end end