diff --git a/lib/datadog/ci/context.rb b/lib/datadog/ci/context.rb new file mode 100644 index 00000000..93a47158 --- /dev/null +++ b/lib/datadog/ci/context.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require "datadog/core/utils/forking" + +module Datadog + module CI + class Context + include Core::Utils::Forking + + attr_reader \ + :active_test + + def initialize(test: nil) + @active_test = test + end + + # Creates a copy of the context, when forked. + def fork_clone + # forked_session = @active_session && @active_session.fork_clone + # do not preserves the active test across forks + self.class.new + end + end + end +end diff --git a/lib/datadog/ci/context_provider.rb b/lib/datadog/ci/context_provider.rb new file mode 100644 index 00000000..2ddafc14 --- /dev/null +++ b/lib/datadog/ci/context_provider.rb @@ -0,0 +1,62 @@ +require "datadog/core/utils/sequence" + +require_relative "context" + +module Datadog + module CI + # Provider is a default context provider that retrieves + # all contexts from the current fiber-local storage. It is suitable for + # synchronous programming. + # + # @see https://ruby-doc.org/core-3.1.2/Thread.html#method-i-5B-5D Thread attributes are fiber-local + class ContextProvider + # Initializes the default context provider with a fiber-bound context. + def initialize + @context = FiberLocalContext.new + end + + # Sets the current context. + def context=(ctx) + @context.local = ctx + end + + # Return the local context. + def context(key = nil) + current_context = key.nil? ? @context.local : @context.local(key) + + current_context.after_fork! do + current_context = self.context = current_context.fork_clone + end + + current_context + end + end + + class FiberLocalContext + def initialize + @key = "datadog_ci_context_#{FiberLocalContext.next_instance_id}".to_sym + + self.local = Context.new + end + + # Override the fiber-local context with a new context. + def local=(ctx) + Thread.current[@key] = ctx + end + + # Return the fiber-local context. + def local(storage = Thread.current) + storage[@key] ||= Context.new + end + + UNIQUE_INSTANCE_MUTEX = Mutex.new + UNIQUE_INSTANCE_GENERATOR = Datadog::Core::Utils::Sequence.new + + private_constant :UNIQUE_INSTANCE_MUTEX, :UNIQUE_INSTANCE_GENERATOR + + def self.next_instance_id + UNIQUE_INSTANCE_MUTEX.synchronize { UNIQUE_INSTANCE_GENERATOR.next } + end + end + end +end diff --git a/sig/datadog/ci/context.rbs b/sig/datadog/ci/context.rbs new file mode 100644 index 00000000..b6d50685 --- /dev/null +++ b/sig/datadog/ci/context.rbs @@ -0,0 +1,14 @@ +module Datadog + module CI + class Context + @active_test: untyped + + include Core::Utils::Forking + + attr_reader active_test: untyped + + def initialize: (?test: untyped?) -> void + def fork_clone: () -> untyped + end + end +end diff --git a/sig/datadog/ci/context_provider.rbs b/sig/datadog/ci/context_provider.rbs new file mode 100644 index 00000000..40483f7f --- /dev/null +++ b/sig/datadog/ci/context_provider.rbs @@ -0,0 +1,24 @@ +module Datadog + module CI + class ContextProvider + @context: untyped + def initialize: () -> void + def context=: (untyped ctx) -> untyped + def context: (?untyped? key) -> untyped + end + + class FiberLocalContext + @key: untyped + + def initialize: () -> void + def local=: (untyped ctx) -> untyped + def local: (?untyped storage) -> untyped + + UNIQUE_INSTANCE_MUTEX: untyped + + UNIQUE_INSTANCE_GENERATOR: untyped + + def self.next_instance_id: () -> untyped + end + end +end