/* Copyright (c) 2023 Contrast Security, Inc. See * https://www.contrastsecurity.com/enduser-terms-0317a for more details. */ #include "cs__scope.h" #include "../cs__common/cs__common.h" #include #include #include /*----------------------------------------- | Calls to Contrast modules and classes ------------------------------------------*/ VALUE contrast, agent, components; VALUE scope_interface, scope_inst_methods; VALUE scope_mod, scope_klass; /*--------- | Arrays ----------*/ static char truthy_arr[3][5] = {"true", "True", "TRUE"}; /*---------- | Helpers -----------*/ /* Declare new c mutex for scope locking: */ pthread_mutex_t c_mutex; /** * @brief get scope for ec or create new * * @note [Ruby definition] EXECUTION_CONTEXT[Fiber.current] ||= * Contrast::Agent::Scope.new * * @return VALUE [Contrast::Agent::Scope] */ VALUE get_ec() { VALUE ec, scope_inst, new_inst, keys, fiber; ec = rb_const_get(scope_mod, rb_intern(rb_const_ec)); scope_inst = rb_hash_aref(ec, rb_fiber_current()); keys = rb_const_get(scope_mod, rb_intern(rb_const_ec_keys)); fiber = rb_fiber_current(); if (RB_TYPE_P(scope_inst, T_NIL)) { new_inst = rb_new_c_scope(); rb_ary_push(keys, fiber); rb_hash_aset(ec, fiber, new_inst); return new_inst; } else { return scope_inst; } } /** * @brief create new Scope class instance * * @return VALUE [Contrast::Agent::Scope] */ VALUE rb_new_c_scope() { return rb_class_new_instance(0, 0, scope_klass); } /** * @brief Increases scope value by 1. * * @param scope int * @return int */ int scope_increase(int scope) { int inc = scope; inc = inc + 1; return INT2FIX(inc); } /** * @brief Decreases scope value by 1. * * @param scope int * @return int */ int scope_decrease(int scope) { int inc = scope; inc = inc - 1; return INT2FIX(inc); } /** * @brief returns Qfalse or Qtrue if is in defined scope * * @param scope int * @return VALUE */ VALUE is_in_scope(int scope) { if (scope > 0) { return Qtrue; } else if (scope <= 0) { return Qfalse; } } /** * @error: raise NoMethodError, "Scope '#{ name.inspect }' is not registered as * a scope." */ void rb_raise_scope_no_method_err(const VALUE method_scope_sym) { rb_raise(rb_eNoMethodError, "Scope ':%" PRIsVALUE "' is not registered as a scope.", rb_sym_to_s(method_scope_sym)); } /*--------------------------------- | @class Contrast::Agent::Scope ----------------------------------*/ /** * @brief sets scope instance variables. * * @param self VALUE * @param args VALUE * @return VALUE self * * @note def initialize * @contrast_scope = 0 * @deserialization_scope = 0 * @split_scope = 0 * end */ VALUE contrast_scope_klass_init(VALUE self, VALUE args) { rb_iv_set(self, rb_iv_cntr_scope, contrast_scope_application_update()); rb_iv_set(self, rb_iv_dslr_scope, INT2FIX(0)); rb_iv_set(self, rb_iv_split_scope, INT2FIX(0)); return self; } /** * @brief This function will determine if the Application Scope flag is set. * If the value set is not valid the Contrast Scope with start with 0. * If the value is validated, then the Contrast Scope will be set to 1. * If the value is nil the Contrast Scope will be set as default => 0. * * To be valid app scope must be one of the truthy_arr: ["true", "True", "TRUE", * "1"] */ VALUE contrast_scope_application_update() { /** * ENV variables are null-terminated strings. * * getenv() returns a pointer to a string within the environment list. * The caller must take care not to modify this string, * since that would change the environment of the process. * * [ Do not touch, do not free. We don't control it! ] */ char *eVar = getenv(c_const_ctr_agent_app_scope); /* check to see if the ENV variable is set */ return INT2FIX(env_var_set(eVar)); } /** * @brief Determines if the Contrast Scope should be set with the Trap Context. * @return 1 if set, 0 if not set. */ int contrast_scope_with_trap_context() { char *eVar = getenv(c_const_ctr_agent_scope_trap_context); return env_var_set(eVar); } /** * @brief Checks to see if the ENV variable is set. * @param args VALUE [String] ENV variable name. * @return 1 if set, 0 if not set. */ int env_var_set(char *eVar) { VALUE env_var = eVar; /* check to see if the ENV variable is set */ if (RTEST(env_var)) { /* Application Scope is set*/ int i; for (i = 0; i < sizeof(truthy_arr) / sizeof(truthy_arr[0]); i++) { if (strcmp(eVar, truthy_arr[i]) == 0) { return 1; } } /* this covers all of the false values */ return 0; } else { /* Application Scope is not set ( NULL )*/ return 0; } } /** * @brief Check if we are in contrast scope. * @note @method in_contrast_scope? * @param self VALUE * @param args VALUE * @return VALUE [Boolean] check if we are in contrast scope * if the scope is above 0 return true. * * @note def in_contrast_scope? * @contrast_scope.positive? * end */ VALUE in_cntr_scope(VALUE self, VALUE args) { return is_in_scope(FIX2INT(rb_iv_get(self, rb_iv_cntr_scope))); } /** * @brief Enters contrast scope. * * @param self VALUE * @param args VALUE * @return VALUE [Integer] contrast scope increased. * * @note def enter_contrast_scope! * @contrast_scope += 1 * end */ VALUE enter_cntr_scope(VALUE self, VALUE args) { int scope = FIX2INT(rb_iv_get(self, rb_iv_cntr_scope)); rb_iv_set(self, rb_iv_cntr_scope, scope_increase(scope)); return rb_iv_get(self, rb_iv_cntr_scope); } /** * @brief Exits contrast scope. * * @param self VALUE * @param args VALUE * @return VALUE [Integer] contrast scope decreased. * * @note def exit_contrast_scope! * @contrast_scope -= 1 * end */ VALUE exit_cntr_scope(VALUE self, VALUE args) { int scope = FIX2INT(rb_iv_get(self, rb_iv_cntr_scope)); rb_iv_set(self, rb_iv_cntr_scope, scope_decrease(scope)); return rb_iv_get(self, rb_iv_cntr_scope); } /** * @brief Check if we are in deserialization scope. * * @param self VALUE * @param args VALUE * @return VALUE [Boolean] check if we are in deserialization scope * if the scope is above 0 return true. * * @note def in_deserialization_scope? * @deserialization_scope.positive? * end */ VALUE in_dslr_scope(VALUE self, VALUE args) { return is_in_scope(FIX2INT(rb_iv_get(self, rb_iv_dslr_scope))); } /** * @brief Enters deserialization scope. * * @param self VALUE * @param args VALUE * @return VALUE [Integer] deserialization scope increased. * * @note def enter_deserialization_scope! * @deserialization_scope += 1 * end */ VALUE enter_dsrl_scope(VALUE self, VALUE args) { int scope = FIX2INT(rb_iv_get(self, rb_iv_dslr_scope)); rb_iv_set(self, rb_iv_dslr_scope, scope_increase(scope)); return rb_iv_get(self, rb_iv_dslr_scope); } /** * @brief Exits deserialization scope. * * @param self VALUE * @param args VALUE * @return VALUE [Integer] deserialization scope decreased. * * @note def enter_deserialization_scope! * @deserialization_scope += 1 * end */ VALUE exit_dsrl_scope(VALUE self, VALUE args) { int scope = FIX2INT(rb_iv_get(self, rb_iv_dslr_scope)); rb_iv_set(self, rb_iv_dslr_scope, scope_decrease(scope)); return rb_iv_get(self, rb_iv_dslr_scope); } /** * @brief Check if we are in split scope. * * @param self VALUE * @param args VALUE * @return VALUE [Boolean] check if we are in split scope * if the scope is above 0 return true. * * @note def in_split_scope? * @split_scope.positive? * end */ VALUE in_split_scope(VALUE self, VALUE args) { return is_in_scope(FIX2INT(rb_iv_get(self, rb_iv_split_scope))); } /** * @brief Enters split scope. * * @param self VALUE * @param args VALUE * @return VALUE [Integer] split scope increased. * * @note def enter_split_scope! * @split_scope += 1 * end */ VALUE enter_split_scope(VALUE self, VALUE args) { int scope = FIX2INT(rb_iv_get(self, rb_iv_split_scope)); rb_iv_set(self, rb_iv_split_scope, scope_increase(scope)); return rb_iv_get(self, rb_iv_split_scope); } /** * @brief Exits split scope. * * @param self VALUE * @param args VALUE * @return VALUE [Integer] split scope decreased. * * @note def enter_split_scope! * @split_scope -= 1 * end */ VALUE exit_split_scope(VALUE self, VALUE args) { int scope = FIX2INT(rb_iv_get(self, rb_iv_split_scope)); rb_iv_set(self, rb_iv_split_scope, scope_decrease(scope)); return rb_iv_get(self, rb_iv_split_scope); } /** * @brief Returns split scope current depth. * * @param self VALUE * @param args VALUE * @return VALUE [Integer] * * @note def split_scope_depth * @split_scope * end */ VALUE split_scope_depth(VALUE self, VALUE args) { return rb_iv_get(self, rb_iv_split_scope); } /** * Static methods to be used, the cases are defined by the usage from the above * methods if more methods are added - please extend the case statements as they * are no longed dynamic. */ /** * @brief Check if we are in specific scope. * * @param self VALUE * @param method_scope_sym VALUE [Symbol] scope symbol representing scope to * check. * @return VALUE [Boolean] check if we are in passed scope. * @error: NoMethodError * * @note def in_scope? name * case name * when :contrast * in_contrast_scope? * when :deserialization * in_deserialization_scope? * when :split * in_split_scope? * else * raise NoMethodError, "Scope '#{ name.inspect }' is not registered * as a scope." end end */ VALUE scope_klass_in_scope(VALUE self, VALUE method_scope_sym) { VALUE in_scope = Qnil; if (method_scope_sym == rb_sym_contrast) { /* in_contrast_scope? */ in_scope = in_cntr_scope(self, 0); } else if (method_scope_sym == rb_sym_deserialization) { /* in_deserialization_scope? */ in_scope = in_dslr_scope(self, 0); } else if (method_scope_sym == rb_sym_split) { /* in_split_scope? */ in_scope = in_split_scope(self, 0); } else { rb_raise_scope_no_method_err(method_scope_sym); } return in_scope; } /** * @brief Enters specific scope. * * @param self VALUE * @param method_scope_sym VALUE [Symbol] scope symbol representing scope to * enter. * @return VALUE [Boolean] entered scope value increased. * * @note def enter_scope! name * case name * when :contrast * enter_contrast_scope! * when :deserialization * enter_deserialization_scope! * when :split * enter_split_scope! * else * raise NoMethodError, "Scope '#{ name.inspect }' is not registered as * a scope." end end */ VALUE scope_klass_enter_scope(VALUE self, VALUE method_scope_sym) { VALUE enter_scope = Qnil; if (method_scope_sym == rb_sym_contrast) { enter_scope = enter_cntr_scope(self, 0); } else if (method_scope_sym == rb_sym_deserialization) { enter_scope = enter_dsrl_scope(self, 0); } else if (method_scope_sym == rb_sym_split) { enter_scope = enter_split_scope(self, 0); } else { rb_raise_scope_no_method_err(method_scope_sym); } return enter_scope; } /** * @brief Exits specific scope. * * @param self VALUE * @param method_scope_sym VALUE [Symbol] scope symbol representing scope to * exit. * @return VALUE [Boolean] entered scope value decreased. * * @note * def exit_scope! name * case name * when :contrast * exit_contrast_scope! * when :deserialization * exit_deserialization_scope! * when :split * exit_split_scope! * else * raise NoMethodError, "Scope '#{ name.inspect }' is not registered as a * scope." end end */ VALUE scope_klass_exit_scope(VALUE self, VALUE method_scope_sym) { VALUE exit_scope = Qnil; if (method_scope_sym == rb_sym_contrast) { exit_scope = exit_cntr_scope(self, 0); } else if (method_scope_sym == rb_sym_deserialization) { exit_scope = exit_dsrl_scope(self, 0); } else if (method_scope_sym == rb_sym_split) { exit_scope = exit_split_scope(self, 0); } else { rb_raise_scope_no_method_err(method_scope_sym); } return exit_scope; } /*----------------------------------------- | @class Contrast::Components::Interface ------------------------------------------*/ /** * @brief init set new scope for current fiber * * @param self VALUE * @param args VALUE * @return VALUE [Hash Scope>] ec * * @note EXECUTION_CONTEXT[Fiber.current] = Contrast::Agent::Scope.new */ VALUE contrast_scope_interface_init(VALUE self, VALUE args) { VALUE ec = rb_const_get(scope_mod, rb_intern(rb_const_ec)); rb_hash_aset(ec, rb_fiber_current(), rb_new_c_scope()); return ec; } /** * @brief This returns the scope governing the current execution context. Use * this sparingly, preferring the instance & class methods to access and query * scope, rather than interacting with the scope object directly. * * Alternative to Monitor => mutex.synchronize * rb_mutex_new(void) * rb_mutex_synchronize (VALUE mutex, VALUE(*func)(VALUE arg), VALUE arg) * Mutex.synchronize do ... end goes here: * * @param self VALUE * @param args VALUE * @return VALUE * * @note def scope_for_current_ec * MONITOR.synchronize do * return EXECUTION_CONTEXT[Fiber.current] ||= * Contrast::Agent::Scope.new end end */ VALUE contrast_scope_for_current_ec(VALUE self, VALUE args) { /* synchronize */ VALUE mutex = rb_const_get(scope_mod, rb_intern(rb_const_mon)); if (contrast_scope_with_trap_context()) { /** * trap context safety: * * If mutex synchonize is called inside a trap context, * it will raise a thread error. To avoid that, there are * different approaches as to trap all signal in a loop, * but that is not a good idea, since the scope is checked * once, and only one signal could be trapped. Another way * is to make new thread around the mutex synchronization * call, but this will create a new execution context and * the returned scope will be different. * * Most of the thread error occur randomly whenever GC is * started. We cannot detect, When the GC is started, it * will stop all current Ruby code execution while running. * * Instead we call the `get_ec()` directly since Ruby have * GVL (Global VM Lock) when calling it's C API therefore * This should be safe called from Ruby land. Just in case * this is a feature flag, so that only be used when thread * safety could be traded for signal safety. * * This is still experimental as relies ong the GLV and the * Ruby VM is not thread safe by default. Various problems * might occur, when the mutex is in Ruby and called from * different thread, at once, but since the mutex stays in * the C API context, this might work fine. Still use only * when unusual conditions are met. * * In addition we could use C mutex lock just in case. Ruby * Threads under the hood are C threads (pthread), so they * should be treated as such. In theory only one thread can * access this code at a time. */ pthread_mutex_lock(&c_mutex); VALUE res = get_ec(); pthread_mutex_unlock(&c_mutex); return res; } else { return rb_mutex_synchronize(mutex, get_ec, 0); } } /*-------------------------------------------------------- | @module Contrast::Components::Scope::InstanceMethods ---------------------------------------------------------*/ /** * @brief Iterates over the method policy's scopes and enters in each one. * * @param self VALUE * @param scopes_to_enter VALUE [Array] Scopes form * method_policy#scopes_to_enter for the scope current method policy * @return VALUE [Array] scopes_to_enter * * @note def enter_method_scope! scopes_to_enter * scopes_to_enter.each do |scope| * enter_scope!(scope) * end * end */ VALUE inst_methods_enter_method_scope(VALUE self, VALUE scopes_to_enter) { VALUE scopes_ary, scope; scopes_ary = rb_ary_dup(scopes_to_enter); scope = rb_ary_pop(scopes_ary); while (!RB_TYPE_P(scope, T_NIL)) { inst_methods_enter_scope(self, scope); scope = rb_ary_pop(scopes_ary); } return scopes_to_enter; } /** * @brief Iterates over the method policy's scopes and exits each one. * * @param self VALUE * @param scopes_to_exit VALUE [Array] Scopes form * method_policy#scopes_to_exit for the scope current method policy * @return VALUE [Array] scopes_to_exit * * @note def enter_method_scope! scopes_to_exit * scopes_to_exit.each do |scope| * enter_scope!(scope) * end * end */ VALUE inst_methods_exit_method_scope(VALUE self, VALUE scopes_to_exit) { VALUE scopes_ary, scope; scopes_ary = rb_ary_dup(scopes_to_exit); scope = rb_ary_pop(scopes_ary); while (!RB_TYPE_P(scope, T_NIL)) { inst_methods_exit_scope(self, scope); scope = rb_ary_pop(scopes_ary); } return scopes_to_exit; } /** * For the InstanceMethods we need to call all the scope methods from the * current ec context All methods bellow are with same names as * Contrast::Agent::Scope class with the difference that they act as forwarders: * def in_contrast_scope? * scope_for_current_ec.in_contrast_scope? * end */ /** * @brief scope_for_current_ec.in_contrast_scope? * * @param self VALUE * @param args VALUE * @return VALUE */ VALUE inst_methods_in_cntr_scope(VALUE self, VALUE args) { return is_in_scope(FIX2INT( rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_cntr_scope))); } /** * @brief scope_for_current_ec.enter_contrast_scope! * * @param self VALUE * @param args VALUE * @return VALUE */ VALUE inst_methods_enter_cntr_scope(VALUE self, VALUE args) { int scope = FIX2INT( rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_cntr_scope)); rb_iv_set(contrast_scope_for_current_ec(self, 0), rb_iv_cntr_scope, scope_increase(scope)); return rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_cntr_scope); } /** * @brief scope_for_current_ec.exit_contrast_scope! * * @param self VALUE * @param args VALUE * @return VALUE */ VALUE inst_methods_exit_cntr_scope(VALUE self, VALUE args) { int scope = FIX2INT( rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_cntr_scope)); rb_iv_set(contrast_scope_for_current_ec(self, 0), rb_iv_cntr_scope, scope_decrease(scope)); return rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_cntr_scope); } /** * @brief scope_for_current_ec.in_split_scope? * * @param self VALUE * @param args VALUE * @return VALUE */ VALUE inst_methods_in_split_scope(VALUE self, VALUE args) { return is_in_scope(FIX2INT( rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_split_scope))); } /** * @brief scope_for_current_ec.enter_split_scope! * * @param self VALUE * @param args VALUE * @return VALUE */ VALUE inst_methods_enter_split_scope(VALUE self, VALUE args) { int scope = FIX2INT( rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_split_scope)); return rb_iv_set(contrast_scope_for_current_ec(self, 0), rb_iv_split_scope, scope_increase(scope)); } /** * @brief scope_for_current_ec.exit_split_scope! * * @param self VALUE * @param args VALUE * @return VALUE */ VALUE inst_methods_exit_split_scope(VALUE self, VALUE args) { int scope = FIX2INT( rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_split_scope)); rb_iv_set(contrast_scope_for_current_ec(self, 0), rb_iv_split_scope, scope_decrease(scope)); return rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_split_scope); } /** * @brief Get Deserialization scope iv. * * @param self * @param args * @return VALUE */ VALUE inst_methods_split_scope_depth(VALUE self, VALUE args) { return rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_split_scope); } /** * @brief scope_for_current_ec.in_deserialization_scope? * * @param self VALUE * @param args VALUE * @return VALUE */ VALUE inst_methods_in_dsrl_scope(VALUE self, VALUE args) { return is_in_scope(FIX2INT( rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_dslr_scope))); } /** * @brief scope_for_current_ec.enter_deserialization_scope! * * @param self VALUE * @param args VALUE * @return VALUE */ VALUE inst_methods_enter_dsrl_scope(VALUE self, VALUE args) { int scope = FIX2INT( rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_dslr_scope)); rb_iv_set(contrast_scope_for_current_ec(self, 0), rb_iv_dslr_scope, scope_increase(scope)); return rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_dslr_scope); } /** * @brief scope_for_current_ec.exit_deserialization_scope! * * @param self VALUE * @param args VALUE * @return VALUE */ VALUE inst_methods_exit_dsrl_scope(VALUE self, VALUE args) { int scope = FIX2INT( rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_dslr_scope)); rb_iv_set(contrast_scope_for_current_ec(self, 0), rb_iv_dslr_scope, scope_decrease(scope)); return rb_iv_get(contrast_scope_for_current_ec(self, 0), rb_iv_dslr_scope); } /** * @brief Check to see if we are in scope by parameter symbol passed. Raises * error if symbol is not valid. * * @param self VALUE * @param method_scope_sym VALUE [Symbol] * @error: rb_raise_scope_no_method_err [NoMethodError] * @return VALUE */ VALUE inst_methods_in_scope(VALUE self, VALUE method_scope_sym) { if (method_scope_sym == rb_sym_contrast) { inst_methods_in_cntr_scope(self, 0); } else if (method_scope_sym == rb_sym_deserialization) { inst_methods_in_dsrl_scope(self, 0); } else if (method_scope_sym == rb_sym_split) { inst_methods_in_split_scope(self, 0); } else { rb_raise_scope_no_method_err(method_scope_sym); } } /** * @brief Enters scope by parameter symbol passed. Raises error if symbol is not * valid. * * @param self VALUE * @param method_scope_sym VALUE [Symbol] * @error: rb_raise_scope_no_method_err [NoMethodError] * @return VALUE */ VALUE inst_methods_enter_scope(VALUE self, VALUE method_scope_sym) { if (method_scope_sym == rb_sym_contrast) { inst_methods_enter_cntr_scope(self, 0); } else if (method_scope_sym == rb_sym_deserialization) { inst_methods_enter_dsrl_scope(self, 0); } else if (method_scope_sym == rb_sym_split) { inst_methods_enter_split_scope(self, 0); } else { rb_raise_scope_no_method_err(method_scope_sym); } } /** * @brief Exits scope by parameter symbol passed. Raises error if symbol is not * valid. * * @param self VALUE * @param method_scope_sym VALUE [Symbol] * @error: rb_raise_scope_no_method_err [NoMethodError] * @return VALUE */ VALUE inst_methods_exit_scope(VALUE self, VALUE method_scope_sym) { if (method_scope_sym == rb_sym_contrast) { inst_methods_exit_cntr_scope(self, 0); } else if (method_scope_sym == rb_sym_deserialization) { inst_methods_exit_dsrl_scope(self, 0); } else if (method_scope_sym == rb_sym_split) { inst_methods_exit_split_scope(self, 0); } else { rb_raise_scope_no_method_err(method_scope_sym); } } /** * @brief Sweeps and cleans any unused Execution Contexts. * TODO: RUBY-534, #sweep_dead_ecs compensates for a lack of weak tables. when * we can use WeakRef, we should investigate removing this call and instead use * the WeakRef for the Execution Context's Keys or using our Finalizers Hash for * Fibers * * @param self VALUE * @param args VALUE * @return VALUE */ VALUE scope_mod_sweep_dead_ecs(VALUE self, VALUE args) { VALUE mutex, ec, ec_keys, key, test; mutex = rb_const_get(scope_mod, rb_intern(rb_const_mon)); ec = rb_const_get(scope_mod, rb_intern(rb_const_ec)); ec_keys = rb_const_get(scope_mod, rb_intern(rb_const_ec_keys)); /* Check if the key is dead (terminated fiber) and delete if true. */ int i = 0; int size = rb_hash_size(ec_keys); for (i = 0; i < size; ++i) { key = rb_ary_entry(ec_keys, i); test = key; if (!RB_TYPE_P(key, T_NIL)) { if (!rb_fiber_alive_p(key)) { rb_hash_delete(ec, key); } } } return ec; } void Init_cs__scope() { /* Init new mutex handle */ pthread_mutex_init(&c_mutex, NULL); /* ivs */ rb_iv_cntr_scope = "@contrast_scope"; rb_iv_dslr_scope = "@deserialization_scope"; rb_iv_split_scope = "@split_scope"; /* constants */ rb_const_mon = "MONITOR"; rb_const_ec = "EXECUTION_CONTEXT"; rb_const_ec_keys = "EC_KEYS"; c_const_ctr_agent_app_scope = "CONTRAST__AGENT__RUBY__APPLICATION_SCOPE"; c_const_ctr_agent_scope_trap_context = "CONTRAST__AGENT__SCOPE__WITH_TRAP_CONTEXT"; /* Symbols */ rb_sym_scope_mod = rb_intern("Scope"); rb_sym_contrast = ID2SYM(rb_intern("contrast")); rb_sym_deserialization = ID2SYM(rb_intern("deserialization")); rb_sym_split = ID2SYM(rb_intern("split")); /* method names */ rb_method_name_init = "initialize"; rb_method_name_in_scope = "in_scope?"; rb_method_name_enter_scope = "enter_scope!"; rb_method_name_exit_scope = "exit_scope!"; rb_method_name_scope_for_current_ec = "scope_for_current_ec"; rb_method_name_in_cntr_scope = "in_contrast_scope?"; rb_method_name_enter_cntr_scope = "enter_contrast_scope!"; rb_method_name_exit_cntr_scope = "exit_contrast_scope!"; rb_method_name_in_dslr_scope = "in_deserialization_scope?"; rb_method_name_enter_dslr_scope = "enter_deserialization_scope!"; rb_method_name_exit_dslr_scope = "exit_deserialization_scope!"; rb_method_name_in_split_scope = "in_split_scope?"; rb_method_name_enter_split_scope = "enter_split_scope!"; rb_method_name_exit_split_scope = "exit_split_scope!"; rb_method_name_split_scope_depth = "split_scope_depth"; /* Define the new scope modules and objects */ contrast = rb_define_module("Contrast"); /* contrast::Agent */ agent = rb_define_module_under(contrast, "Agent"); /* components => Contrast::Components */ components = rb_define_module_under(contrast, "Components"); /* scope_mod => Contrast::Components::Scope */ scope_mod = rb_define_module_under(components, "Scope"); /* scope_interface => Contrast::Components::Scope::Interface */ scope_interface = rb_define_class_under(scope_mod, "Interface", rb_cObject); /* scope_inst_methods => Contrast::Components::Scope::InstanceMethods */ scope_inst_methods = rb_define_module_under(scope_mod, "InstanceMethods"); /* scope_klass => Contrast::Agent::Scope */ scope_klass = rb_define_class_under(agent, "Scope", rb_cObject); /** * @class Contrast::Agent::Scope */ /* Instance methods: */ rb_define_method(scope_klass, rb_method_name_init, contrast_scope_klass_init, 0); rb_define_method(scope_klass, rb_method_name_in_cntr_scope, in_cntr_scope, 0); rb_define_method(scope_klass, rb_method_name_enter_cntr_scope, enter_cntr_scope, 0); rb_define_method(scope_klass, rb_method_name_exit_cntr_scope, exit_cntr_scope, 0); rb_define_method(scope_klass, rb_method_name_in_dslr_scope, in_dslr_scope, 0); rb_define_method(scope_klass, rb_method_name_enter_dslr_scope, enter_dsrl_scope, 0); rb_define_method(scope_klass, rb_method_name_exit_dslr_scope, exit_dsrl_scope, 0); rb_define_method(scope_klass, rb_method_name_in_split_scope, in_split_scope, 0); rb_define_method(scope_klass, rb_method_name_enter_split_scope, enter_split_scope, 0); rb_define_method(scope_klass, rb_method_name_exit_split_scope, exit_split_scope, 0); rb_define_method(scope_klass, rb_method_name_split_scope_depth, split_scope_depth, 0); rb_define_method(scope_klass, rb_method_name_in_scope, scope_klass_in_scope, 1); rb_define_method(scope_klass, rb_method_name_enter_scope, scope_klass_enter_scope, 1); rb_define_method(scope_klass, rb_method_name_exit_scope, scope_klass_exit_scope, 1); /** * @class Contrast::Components::Interface */ /* Interface#initialize */ rb_define_method(scope_interface, rb_method_name_init, contrast_scope_interface_init, 0); /* Interface#scope_for_current_ec */ rb_define_method(scope_interface, rb_method_name_scope_for_current_ec, contrast_scope_for_current_ec, 0); /** * @module Contrast::Components::Scope */ /* Constants */ /* Contrast::Components::Scope::EC_KEYS */ rb_const_set(scope_mod, rb_intern(rb_const_ec_keys), rb_ary_new()); /* Contrast::Components::Scope::EXECUTION_CONTEXT => {} */ rb_define_const(scope_mod, rb_const_ec, rb_hash_new()); /* Contrast::Components::Scope::MONITOR => Mutex.new.freeze */ rb_define_const(scope_mod, rb_const_mon, rb_mutex_new()); /** * @module Contrast::Components::Scope::InstanceMethods */ /* InstanceMethods#scope_for_current_ec */ rb_define_method(scope_inst_methods, rb_method_name_scope_for_current_ec, contrast_scope_for_current_ec, 0); /* Forwarders */ rb_define_method(scope_inst_methods, rb_method_name_in_cntr_scope, inst_methods_in_cntr_scope, 0); rb_define_method(scope_inst_methods, rb_method_name_enter_cntr_scope, inst_methods_enter_cntr_scope, 0); rb_define_method(scope_inst_methods, rb_method_name_exit_cntr_scope, inst_methods_exit_cntr_scope, 0); rb_define_method(scope_inst_methods, rb_method_name_in_dslr_scope, inst_methods_in_dsrl_scope, 0); rb_define_method(scope_inst_methods, rb_method_name_enter_dslr_scope, inst_methods_enter_dsrl_scope, 0); rb_define_method(scope_inst_methods, rb_method_name_exit_dslr_scope, inst_methods_exit_dsrl_scope, 0); rb_define_method(scope_inst_methods, rb_method_name_in_split_scope, inst_methods_in_split_scope, 0); rb_define_method(scope_inst_methods, rb_method_name_enter_split_scope, inst_methods_enter_split_scope, 0); rb_define_method(scope_inst_methods, rb_method_name_exit_split_scope, inst_methods_exit_split_scope, 0); rb_define_method(scope_inst_methods, rb_method_name_split_scope_depth, inst_methods_split_scope_depth, 0); rb_define_method(scope_inst_methods, rb_method_name_in_scope, inst_methods_in_scope, 1); rb_define_method(scope_inst_methods, rb_method_name_enter_scope, inst_methods_enter_scope, 1); rb_define_method(scope_inst_methods, rb_method_name_exit_scope, inst_methods_exit_scope, 1); /** * @module Contrast::Components::Scope */ rb_define_singleton_method(scope_mod, "sweep_dead_ecs", scope_mod_sweep_dead_ecs, 0); /* Contrast::Components::Scope#cs__with_app_scope? */ rb_define_singleton_method(scope_mod, "cs__with_app_scope?", contrast_scope_application_update, 0); /** * @module Contrast::Components::Scope::InstanceMethods */ rb_define_method(scope_inst_methods, "contrast_enter_method_scopes!", inst_methods_enter_method_scope, 1); rb_define_method(scope_inst_methods, "contrast_exit_method_scopes!", inst_methods_exit_method_scope, 1); /* free the c_mutex */ pthread_mutex_destroy(&c_mutex); }