Add bin and edit workflow
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 9s

This commit is contained in:
2026-09-16 13:11:16 -06:00
parent c8ac4fcae5
commit 4cee170d66
17576 changed files with 895740 additions and 2 deletions
@@ -0,0 +1,387 @@
# = Multiton
#
# == Synopsis
#
# Multiton design pattern ensures only one object is allocated for a given state.
#
# The 'multiton' pattern is similar to a singleton, but instead of only one
# instance, there are several similar instances. It is useful when you want to
# avoid constructing objects many times because of some huge expense (connecting
# to a database for example), require a set of similar but not identical
# objects, and cannot easily control how many times a contructor may be called.
#
# class SomeMultitonClass
# include Multiton
# attr :arg
# def initialize(arg)
# @arg = arg
# end
# end
#
# a = SomeMultitonClass.new(4)
# b = SomeMultitonClass.new(4) # a and b are same object
# c = SomeMultitonClass.new(2) # c is a different object
#
# == Previous Behavior
#
# In previous versions of Multiton the #new method was made
# private and #instance had to be used in its stay --just like Singleton.
# But this is less desirable for Multiton since Multitions can
# have multiple instances, not just one.
#
# So instead Multiton now defines #create as a private alias of
# the original #new method (just in case it is needed) and then
# defines #new to handle the multiton; #instance is provided
# as an alias for it.
#
#--
# So if you must have the old behavior, all you need do is re-alias
# #new to #create and privatize it.
#
# class SomeMultitonClass
# include Multiton
# alias_method :new, :create
# private :new
# ...
# end
#
# Then only #instance will be available for creating the Multiton.
#++
#
# == How It Works
#
# A pool of objects is searched for a previously cached object,
# if one is not found we construct one and cache it in the pool
# based on class and the args given to the contructor.
#
# A limitation of this approach is that it is impossible to
# detect if different blocks were given to a contructor (if it takes a
# block). So it is the constructor arguments _only_ which determine
# the uniqueness of an object. To workaround this, define the _class_
# method ::multiton_id.
#
# def SomeMultitonClass.multiton_id(*args, &block)
# # ...
# end
#
# Which should return a hash key used to identify the object being
# constructed as (not) unique.
#
# == Authors
#
# * Christoph Rippel
# * Thomas Sawyer
#
# = Copying
#
# Copyright (c) 2007 Christoph Rippel, Thomas Sawyer
#
# Ruby License
#
# This module is free software. You may use, modify, and/or redistribute this
# software under the same terms as Ruby.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.
require 'thread'
# = Multiton
#
# Multiton design pattern ensures only one object is allocated for a given state.
#
# The 'multiton' pattern is similar to a singleton, but instead of only one
# instance, there are several similar instances. It is useful when you want to
# avoid constructing objects many times because of some huge expense (connecting
# to a database for example), require a set of similar but not identical
# objects, and cannot easily control how many times a contructor may be called.
#
# class SomeMultitonClass
# include Multiton
# attr :arg
# def initialize(arg)
# @arg = arg
# end
# end
#
# a = SomeMultitonClass.new(4)
# b = SomeMultitonClass.new(4) # a and b are same object
# c = SomeMultitonClass.new(2) # c is a different object
#
# == How It Works
#
# A pool of objects is searched for a previously cached object,
# if one is not found we construct one and cache it in the pool
# based on class and the args given to the contructor.
#
# A limitation of this approach is that it is impossible to
# detect if different blocks were given to a contructor (if it takes a
# block). So it is the constructor arguments _only_ which determine
# the uniqueness of an object. To workaround this, define the _class_
# method ::multiton_id.
#
# def SomeMultitonClass.multiton_id(*args, &block)
# # ...
# end
#
# Which should return a hash key used to identify the object being
# constructed as (not) unique.
module Multiton
# Disable build-in copying methods.
def clone
raise TypeError, "can't clone Multiton #{self}"
##self
end
def dup
raise TypeError, "can't dup Multiton #{self}"
##self
end
# Default marshalling strategy.
protected
def _dump(depth=-1)
Marshal.dump(@multiton_initializer)
end
# Mutex to safely store multiton instances.
class InstanceMutex < Hash #:nodoc:
def initialize
@global = Mutex.new
end
def initialized(arg)
store(arg, DummyMutex)
end
def (DummyMutex = Object.new).synchronize
yield
end
def default(arg)
@global.synchronize{ fetch(arg){ store(arg, Mutex.new) } }
end
end
# Multiton can be included in another module, in which case that module
# effectively becomes a multiton behavior distributor too. This is why we
# propogate #included to the base module by putting it in another module.
#
#--
# def append_features(mod)
# # help out people counting on transitive mixins
# unless mod.instance_of?(Class)
# raise TypeError, "Inclusion of Multiton in module #{mod}"
# end
# super
# end
#++
module Inclusive
private
def included(base)
class << base
##alias_method(:new!, :new) unless method_defined?(:new!)
## gracefully handle multiple inclusions of Multiton
unless include?(Multiton::MetaMethods)
alias_method :new!, :new
private :allocate #, :new
include Multiton::MetaMethods
if method_defined?(:marshal_dump)
undef_method :marshal_dump
warn "warning: marshal_dump was undefined since it is incompatible with the Multiton pattern"
end
end
end
end
end
extend Inclusive
#
module MetaMethods
include Inclusive
def instance(*e, &b)
arg = multiton_id(*e, &b)
multiton_instance.fetch(arg) do
multiton_mutex[arg].synchronize do
multiton_instance.fetch(arg) do
val = multiton_instance[arg] = new!(*e, &b) #new(*e, &b)
val.instance_variable_set(:@multiton_initializer, e, &b)
multiton_mutex.initialized(arg)
val
end
end
end
end
alias_method :new, :instance
def initialized?(*e, &b)
multiton_instance.key?(multiton_id(*e, &b))
end
protected
def multiton_instance
@multiton_instance ||= Hash.new
end
def multiton_mutex
@multiton_mutex ||= InstanceMutex.new
end
def reinitialize
multiton_instance.clear
multiton_mutex.clear
end
def _load(str)
instance(*Marshal.load(str))
end
private
# Default method to to create a key to cache already constructed
# instances. In the use case MultitonClass.new(e), MultiClass.new(f)
# must be semantically equal if multiton_id(e).eql?(multiton_id(f))
# evaluates to true.
def multiton_id(*e, &b)
e
end
def singleton_method_added(sym)
super
if (sym == :marshal_dump) & singleton_methods.include?('marshal_dump')
raise TypeError, "Don't use marshal_dump - rely on _dump and _load instead"
end
end
end
end
=begin
# TODO: Convert this into a real test and/or benchmark.
if $0 == __FILE__
### Simple marshalling test #######
class A
def initialize(a,*e)
@e = a
end
include Multiton
begin
def self.marshal_dump(depth = -1)
end
rescue => mes
p mes
class << self; undef marshal_dump end
end
end
C = Class.new(A.clone)
s = C.instance('a','b')
raise unless Marshal.load(Marshal.dump(s)) == s
### Interdependent initialization example and threading benchmark ###
class Regular_SymPlane
def self.multiton_id(*e)
a,b = e
(a+b - 1)*(a+b )/2 + (a > b ? a : b)
end
def initialize(a,b)
klass = self.class
if a < b
@l = b > 0 ? klass.instance(a,b-1) : nil
@r = a > 0 ? klass.instance(a-1,b) : nil
else
@l = a > 0 ? klass.instance(a-1,b) : nil
@r = b > 0 ? klass.instance(a,b-1) : nil
end
end
include Multiton
end
def nap
# Thread.pass
sleep(rand(0.01))
end
class SymPlane < Regular_SymPlane
@m = Mutex.new
@count = 0
end
class << SymPlane
attr_reader :count
def reinitialize
super
@m = Mutex.new
@count = 0
end
def inherited(sub_class)
super
sub_class.instance_eval { @m = Mutex.new; @count = 0 }
end
def multiton_id(*e)
nap()
super
end
def new!(*e) # NOTICE!!!
super
ensure
nap()
@m.synchronize { p @count if (@count += 1) % 15 == 0 }
end
def run(k)
threads = 0
max = k * (k+1) / 2
puts ""
while count() < max
Thread.new { threads+= 1; instance(rand(30),rand(30)) }
end
puts "\nThe simulation created #{threads} threads"
end
end
require 'benchmark'
include Benchmark
bmbm do |x|
x.report('Initialize 465 SymPlane instances') { SymPlane.run(30) }
x.report('Reinitialize ') do
sleep 3
SymPlane.reinitialize
end
end
end
=end