celluloid-0.18.0/0000755000004100000410000000000014005764144013611 5ustar www-datawww-datacelluloid-0.18.0/CONDUCT.md0000644000004100000410000000261514005764144015236 0ustar www-datawww-data# Contributor Code of Conduct As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion. Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) celluloid-0.18.0/README.md0000644000004100000410000001104114005764144015065 0ustar www-datawww-data# ![Celluloid][celluloid-logo-image-raw] [![Gem Version][gem-image]][gem-link] [![MIT licensed][license-image]][license-link] [![Build Status][build-image]][build-link] [![Maintained: no][maintained-image]][maintained-link] [![Gitter Chat][gitter-image]][gitter-link] [celluloid-logo-image-raw]: https://raw.github.com/celluloid/celluloid-logos/master/celluloid/celluloid.png [gem-image]: https://badge.fury.io/rb/celluloid.svg [gem-link]: http://rubygems.org/gems/celluloid [build-image]: https://secure.travis-ci.org/celluloid/celluloid.svg?branch=master [build-link]: http://travis-ci.org/celluloid/celluloid [license-image]: https://img.shields.io/badge/license-MIT-blue.svg [license-link]: https://github.com/celluloid/celluloid/blob/master/LICENSE.txt [maintained-image]: https://img.shields.io/maintenance/no/2016.svg [maintained-link]: https://github.com/celluloid/celluloid/issues/779 [gitter-image]: https://badges.gitter.im/badge.svg [gitter-link]: https://gitter.im/celluloid/celluloid Celluloid is a framework for building asynchronous and multithreaded Ruby programs using object-oriented concepts. ## Revival Process Underway `Celluloid` is in the process of being refactored and released back into the wild during `Google Summer of Code`. The next era will not have one individual active maintainer, but a team of collaborators. Going forward, previously dormant maintainer [Donovan Keme](https://github.com/digitalextremist) is returning to support future primary maintainer [Emese Padányi](https://github.com/emesepadanyi) during `GSoC 2020`. Her plan extends past the Summer program, and aims to revive the community and codebase of `Celluloid` together. Backing this process are [Harsh Deep](https://github.com/harsh183) and `GSoC` alumni [Dilum Navanjana](https://github.com/dilumn). We welcome your collaboration and contributions in this massive work. The codebase is being refactored to pursue a stable release with no deprecation warnings, and with this cleaned up: # ![Diagram][celluloid-diagram] *Diagram meticulously developed by [Emese Padányi](https://github.com/emesepadanyi)* [celluloid-diagram]: https://raw.githubusercontent.com/celluloid/celluloid/master/documentation/ClassDiagram-class_diagram.png ### Proudly supported by the best cloud infrastructure provider in the world: [`DigitalOcean`](https://digitalocean.com) ## Discussion - [Gitter Chat][gitter-link] - [Google Group](https://groups.google.com/group/celluloid-ruby) ## Documentation [Please see the Celluloid Wiki](https://github.com/celluloid/celluloid/wiki) for more detailed documentation and usage notes. The following API documentation is also available: * [YARD API documentation](http://rubydoc.info/gems/celluloid/frames) * [Celluloid module (primary API)](http://rubydoc.info/gems/celluloid/Celluloid) * [Celluloid class methods](http://rubydoc.info/gems/celluloid/Celluloid/ClassMethods) * [All Celluloid classes](http://rubydoc.info/gems/celluloid/index) ## Related Projects See also: [Projects Using Celluloid](https://github.com/celluloid/celluloid/wiki/Projects-Using-Celluloid) * [Reel][reel]: An "evented" web server based on `Celluloid::IO` * [DCell][dcell]: The Celluloid actor protocol distributed over 0MQ * [ECell][ecell]: Mesh strategies for `Celluloid` actors distributed over 0MQ * [Celluloid::IO][celluloid-io]: "Evented" IO support for `Celluloid` actors * [Celluloid::ZMQ][celluloid-zmq]: "Evented" 0MQ support for `Celluloid` actors * [Celluloid::DNS][celluloid-dns]: An "evented" DNS server based on `Celluloid::IO` * [Celluloid::SMTP][celluloid-smtp]: An "evented" SMTP server based on `Celluloid::IO` * [nio4r][nio4r]: "New IO for Ruby": high performance IO selectors * [Timers][timers]: A generic Ruby timer library for event-based systems [reel]: https://github.com/celluloid/reel/ [dcell]: https://github.com/celluloid/dcell/ [ecell]: https://github.com/abstractive/ecell/ [celluloid-io]: https://github.com/celluloid/celluloid-io/ [celluloid-zmq]: https://github.com/celluloid/celluloid-zmq/ [celluloid-dns]: https://github.com/celluloid/celluloid-dns/ [celluloid-smtp]: https://github.com/abstractive/celluloid-smtp/ [nio4r]: https://github.com/celluloid/nio4r/ [timers]: https://github.com/celluloid/timers/ ## Contributing to Celluloid - Fork this repository on github - Make your changes and send us a pull request - Pull requests will be reviewed for inclusion in the project ## License Copyright (c) 2011-2018 Tony Arcieri, Donovan Keme. Distributed under the MIT License. See [LICENSE.txt](https://github.com/celluloid/celluloid/blob/master/LICENSE.txt) for further details. celluloid-0.18.0/celluloid.gemspec0000644000004100000410000002022414005764144017132 0ustar www-datawww-data######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: celluloid 0.18.0 ruby lib Gem::Specification.new do |s| s.name = "celluloid".freeze s.version = "0.18.0" s.required_rubygems_version = Gem::Requirement.new(">= 2.0.0".freeze) if s.respond_to? :required_rubygems_version= s.metadata = { "bug_tracker_uri" => "https://github.com/celluloid/celluloid/issues", "changelog_uri" => "https://github.com/celluloid/celluloid/blob/master/CHANGES.md", "documentation_uri" => "https://www.rubydoc.info/gems/celluloid", "homepage_uri" => "https://celluloid.io/", "mailing_list_uri" => "http://groups.google.com/group/celluloid-ruby", "source_code_uri" => "https://github.com/celluloid/celluloid", "wiki_uri" => "https://github.com/celluloid/celluloid/wiki" } if s.respond_to? :metadata= s.require_paths = ["lib".freeze] s.authors = ["Tony Arcieri".freeze, "Donovan Keme".freeze] s.date = "2020-12-06" s.description = "Celluloid enables people to build concurrent programs out of concurrent objects just as easily as they build sequential programs out of sequential objects".freeze s.email = ["bascule@gmail.com".freeze, "code@extremist.digital".freeze] s.files = ["CHANGES.md".freeze, "CONDUCT.md".freeze, "CONTRIBUTING.md".freeze, "LICENSE.txt".freeze, "README.md".freeze, "REFACTOR.md".freeze, "architecture.md".freeze, "examples/basic_usage.rb".freeze, "examples/configurations.rb".freeze, "examples/futures.rb".freeze, "examples/ring.rb".freeze, "examples/simple_pmap.rb".freeze, "examples/stack.rb".freeze, "examples/supervisors_and_registry.rb".freeze, "examples/timers.rb".freeze, "lib/celluloid.rb".freeze, "lib/celluloid/actor.rb".freeze, "lib/celluloid/actor/system.rb".freeze, "lib/celluloid/autostart.rb".freeze, "lib/celluloid/call/async.rb".freeze, "lib/celluloid/call/block.rb".freeze, "lib/celluloid/call/sync.rb".freeze, "lib/celluloid/calls.rb".freeze, "lib/celluloid/cell.rb".freeze, "lib/celluloid/condition.rb".freeze, "lib/celluloid/core_ext.rb".freeze, "lib/celluloid/debug.rb".freeze, "lib/celluloid/exceptions.rb".freeze, "lib/celluloid/future.rb".freeze, "lib/celluloid/group.rb".freeze, "lib/celluloid/group/pool.rb".freeze, "lib/celluloid/group/spawner.rb".freeze, "lib/celluloid/internals/call_chain.rb".freeze, "lib/celluloid/internals/cpu_counter.rb".freeze, "lib/celluloid/internals/handlers.rb".freeze, "lib/celluloid/internals/links.rb".freeze, "lib/celluloid/internals/logger.rb".freeze, "lib/celluloid/internals/method.rb".freeze, "lib/celluloid/internals/properties.rb".freeze, "lib/celluloid/internals/receivers.rb".freeze, "lib/celluloid/internals/registry.rb".freeze, "lib/celluloid/internals/responses.rb".freeze, "lib/celluloid/internals/signals.rb".freeze, "lib/celluloid/internals/stack.rb".freeze, "lib/celluloid/internals/stack/dump.rb".freeze, "lib/celluloid/internals/stack/states.rb".freeze, "lib/celluloid/internals/stack/summary.rb".freeze, "lib/celluloid/internals/task_set.rb".freeze, "lib/celluloid/internals/thread_handle.rb".freeze, "lib/celluloid/internals/uuid.rb".freeze, "lib/celluloid/logging.rb".freeze, "lib/celluloid/logging/incident.rb".freeze, "lib/celluloid/logging/incident_logger.rb".freeze, "lib/celluloid/logging/incident_reporter.rb".freeze, "lib/celluloid/logging/log_event.rb".freeze, "lib/celluloid/logging/ring_buffer.rb".freeze, "lib/celluloid/mailbox.rb".freeze, "lib/celluloid/mailbox/evented.rb".freeze, "lib/celluloid/notifications.rb".freeze, "lib/celluloid/pool.rb".freeze, "lib/celluloid/probe.rb".freeze, "lib/celluloid/proxies.rb".freeze, "lib/celluloid/proxy/abstract.rb".freeze, "lib/celluloid/proxy/actor.rb".freeze, "lib/celluloid/proxy/async.rb".freeze, "lib/celluloid/proxy/block.rb".freeze, "lib/celluloid/proxy/cell.rb".freeze, "lib/celluloid/proxy/future.rb".freeze, "lib/celluloid/proxy/sync.rb".freeze, "lib/celluloid/rspec.rb".freeze, "lib/celluloid/supervision.rb".freeze, "lib/celluloid/supervision/configuration.rb".freeze, "lib/celluloid/supervision/configuration/injections.rb".freeze, "lib/celluloid/supervision/configuration/instance.rb".freeze, "lib/celluloid/supervision/constants.rb".freeze, "lib/celluloid/supervision/container.rb".freeze, "lib/celluloid/supervision/container/behavior.rb".freeze, "lib/celluloid/supervision/container/behavior/pool.rb".freeze, "lib/celluloid/supervision/container/behavior/tree.rb".freeze, "lib/celluloid/supervision/container/injections.rb".freeze, "lib/celluloid/supervision/container/instance.rb".freeze, "lib/celluloid/supervision/container/pool.rb".freeze, "lib/celluloid/supervision/service.rb".freeze, "lib/celluloid/supervision/supervise.rb".freeze, "lib/celluloid/supervision/validation.rb".freeze, "lib/celluloid/supervision/version.rb".freeze, "lib/celluloid/system_events.rb".freeze, "lib/celluloid/task.rb".freeze, "lib/celluloid/task/fibered.rb".freeze, "lib/celluloid/task/threaded.rb".freeze, "lib/celluloid/test.rb".freeze, "lib/celluloid/thread.rb".freeze, "lib/celluloid/version.rb".freeze, "spec/celluloid/actor/system_spec.rb".freeze, "spec/celluloid/actor_spec.rb".freeze, "spec/celluloid/block_spec.rb".freeze, "spec/celluloid/calls_spec.rb".freeze, "spec/celluloid/condition_spec.rb".freeze, "spec/celluloid/evented_mailbox_spec.rb".freeze, "spec/celluloid/future_spec.rb".freeze, "spec/celluloid/group/elastic_spec.rb".freeze, "spec/celluloid/group/pool_spec.rb".freeze, "spec/celluloid/group/spawner_spec.rb".freeze, "spec/celluloid/internals/cpu_counter_spec.rb".freeze, "spec/celluloid/internals/links_spec.rb".freeze, "spec/celluloid/internals/properties_spec.rb".freeze, "spec/celluloid/internals/registry_spec.rb".freeze, "spec/celluloid/internals/stack/dump_spec.rb".freeze, "spec/celluloid/internals/stack/summary_spec.rb".freeze, "spec/celluloid/internals/thread_handle_spec.rb".freeze, "spec/celluloid/internals/uuid_spec.rb".freeze, "spec/celluloid/logging/ring_buffer_spec.rb".freeze, "spec/celluloid/mailbox/evented_spec.rb".freeze, "spec/celluloid/mailbox_spec.rb".freeze, "spec/celluloid/misc/leak_spec.rb".freeze, "spec/celluloid/notifications_spec.rb".freeze, "spec/celluloid/probe_spec.rb".freeze, "spec/celluloid/proxy_spec.rb".freeze, "spec/celluloid/supervision/behavior_spec.rb".freeze, "spec/celluloid/supervision/configuration_spec.rb".freeze, "spec/celluloid/supervision/container_spec.rb".freeze, "spec/celluloid/supervision/instance_spec.rb".freeze, "spec/celluloid/supervision/root_spec.rb".freeze, "spec/celluloid/supervision/supervisor_spec.rb".freeze, "spec/celluloid/task/fibered_spec.rb".freeze, "spec/celluloid/task/threaded_spec.rb".freeze, "spec/celluloid/timer_spec.rb".freeze, "spec/shared/actor_examples.rb".freeze, "spec/shared/group_examples.rb".freeze, "spec/shared/mailbox_examples.rb".freeze, "spec/shared/stack_examples.rb".freeze, "spec/shared/task_examples.rb".freeze, "spec/spec_helper.rb".freeze, "spec/support/configure_rspec.rb".freeze, "spec/support/coverage.rb".freeze, "spec/support/crash_checking.rb".freeze, "spec/support/debugging.rb".freeze, "spec/support/examples/actor_class.rb".freeze, "spec/support/examples/call_class.rb".freeze, "spec/support/examples/container_class.rb".freeze, "spec/support/examples/evented_mailbox_class.rb".freeze, "spec/support/examples/stack_classes.rb".freeze, "spec/support/examples/stack_methods.rb".freeze, "spec/support/examples/subordinate_class.rb".freeze, "spec/support/includer.rb".freeze, "spec/support/logging.rb".freeze, "spec/support/loose_threads.rb".freeze, "spec/support/reset_class_variables.rb".freeze, "spec/support/sleep_and_wait.rb".freeze, "spec/support/stubbing.rb".freeze] s.homepage = "https://github.com/celluloid/celluloid".freeze s.licenses = ["MIT".freeze] s.required_ruby_version = Gem::Requirement.new(">= 2.2.6".freeze) s.rubygems_version = "2.5.2.1".freeze s.summary = "Actor-based concurrent object framework for Ruby".freeze if s.respond_to? :specification_version then s.specification_version = 4 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_runtime_dependency(%q.freeze, ["~> 4"]) else s.add_dependency(%q.freeze, ["~> 4"]) end else s.add_dependency(%q.freeze, ["~> 4"]) end end celluloid-0.18.0/architecture.md0000644000004100000410000001471214005764144016622 0ustar www-datawww-data# Architecture The purpose of this document is to provide an overview of the `Celluloid` project architecture. It is intended as a learning tool for GSoC (Google Summer of Code) and for anyone else interested in peeking under the hood to figure out how Celluloid works. This document will evolve since the people writing it are learning the project too. It will likely evolve in the following manner: 1. Document all major classes by describing the purpose of each one. 2. Document the dependencies between classes described in item 1. 3. Describe the flow of messages between the classes documented in items 1 and 2. 4. Generate a Big Picture overview of the overall Celluloid architecture. 5. Create a flew graphics to depict class dependencies and message flows (perhaps as a directed acyclic graph). ## Document Status The document is working on item 1. ## Required Libraries `logger` `thread` `timeout` `set` ## Require Lifecycle Document the steps taken from the moment `require "celluloid"` is executed by the runtime. The file `celluloid.rb` is read in by the runtime. This file requires some standard libraries like `Thread` and `Set` (see Required Libraries for full list) and initializes the `Celluloid` module namespace. It then sets up a Celluloid singleton class which will contain utility functions that need to be accessed from anywhere. Think of these singleton methods as global methods. These specific methods should be considered private to the library and should not be directly called by user code. From here it continues to `extend` and `include` other modules so that any Ruby object that executes `include Celluloid` as part of its class definition automatically gains all of the Celluloid magic. Next, it defines regular methods within the Celluloid namespace. These are also global methods but they are essentially the public API to the outside world. These methods (such as `current_actor` and `terminate`) can be called by user code. Next we have a list of `require`'d subfiles. This loads the remainder of the library in preparation to start running. Next, the code sets up two global default settings for the `task_class` and `group_class`. I don't know what these are yet but by exposing them this way it's clearly intended for these items to be defined in such a way that user code can override them with use-case-specific code. The code here can also read names from the environment variables to set the defaults. This is likely intended for use by the spec/test system. Lastly, the code registers some methods for shutdown to terminate all actors `at_exit` and then initializes the system. To boot the system, call `require 'celluloid'`. ## Supervision Lifecycle The final call to `Actor::System.new.start` kicks off the top level supervisor. The supervisor container is an actor and it will keep track of all defined actors as they are added for supervision. A user may create an actor that is not supervised, so the top-level supervisor does not necessarily have a reference to every actor in the system. The Registry is also created at this time and lives within the main container. Actors can be added to the registry even if they are not supervised. The two concepts are separate even though the code is somewhat comingled. ## Classes / Modules ### Celluloid Module * Sets up class accessors used throughout/globally such as `logger` * When using `include Celluloid` on a class, it performs the following work during `include`: * Extends `ClassMethods` and `Internals::Properties` * Includes `InstanceMethods` * Sets properties on the class object. * Removes `trap_exit` and `exclusive` if they exist on the singleton class so that Celluloid can redefine them for itself. * Defines class methods inside the `Celluloid` namespace such as `actor?` and `mailbox` and `cores`. These are utility functions. They are defined on the Celluloid singleton. * Provides the entry method `boot` to start up the whole system and its opposite `shutdown` to terminate everything. #### Depends On Classes * Internals::Logger * Internals::CallChain * Actor::System * Celluloid::Mailbox * Thread (primarily for Thread.current to access fiber locals like `:celluloid_actor`) * Future ### ClassMethods Module This class contains class-level methods which are added to every user class that contains `include Celluloid`. * Overrides `new` for the class object. #### Depends On Classes * Cell * Actor * Celluloid ### InstanceMethods Module This module contains instance-level methods which are added to every user class that contains `include Celluloid`. #### Depends on Classes * Actor * Celluloid ### Actor::System class Is created and `start`'ed as part of the entire boot sequence. This class provides essential services utilized by the rest of the library such as the Registry and the top-level Supervisor. #### Lifecycle Creates the `Registry` and initializes the `Celluloid.group_class`. Upon `start` it makes sure that it switches the `Thread.current[:celluloid_actor_system]` to itself, then defines and deploys the `Root` services. #### Depends On Classes * Supervision::Service::Root which is in gem `celluloid-supervision` * Celluloid::Notifications::Fanout * Celluloid::IncidentReporter * Celluloid::Supervision::Service::Public * Celluloid::Actor::Manager * Celluloid * Internals::Registry * Celluloid.group_class ## Gem - celluloid-supervision Necessary for the core system to boot. Really has only two classes/modules of note. One is Supervision::Container which is an actor that keeps track of all actors under supervision. It also handles restart. Two is Supervision::Configuration. This class manages the several different ways that an actor may be placed under supervision, arguments passed to it, and other data. Be careful when reading the code. Each file in the gem relies on open Ruby classes. These files reopen previously defined classes and add more behavior, so to get a complete picture for what the Configuration and Container classes can do you must look through all of the files. While this logical separation of business logic to its own file is neat, it adds to the cognitive load when reading the code since the entire class defintion is spread over multiple files. ### Supervision::Service::Root #### Depends On Classes * Supervision::Container ### Supervision::Container Stores references to Actors on behalf of a Supervisor. This is the only class under the `Supervision` namespace that is an Actor. Every other class is a regular Ruby class. #### Depends On Classes * Supervision::Configuration * celluloid-0.18.0/spec/0000755000004100000410000000000014005764144014543 5ustar www-datawww-datacelluloid-0.18.0/spec/celluloid/0000755000004100000410000000000014005764144016517 5ustar www-datawww-datacelluloid-0.18.0/spec/celluloid/supervision/0000755000004100000410000000000014005764144021105 5ustar www-datawww-datacelluloid-0.18.0/spec/celluloid/supervision/container_spec.rb0000644000004100000410000000346114005764144024432 0ustar www-datawww-dataRSpec.describe Celluloid::Supervision::Container, actor_system: :global do let(:queue_count) { 1 } before do SupervisionContainerHelper.reset! subject # init for easier debugging queue_count.times { SupervisionContainerHelper.pop! } end after do # TODO: hangs without condition on JRuby? subject.terminate if subject.alive? end context "when supervising a single actor" do subject do Class.new(Celluloid::Supervision::Container) do supervise type: MyContainerActor, as: :example end.run!(*registry) end let(:registry) { [] } it "runs applications" do expect(Celluloid::Actor[:example]).to be_running end context "with a private registry" do let(:registry) { Celluloid::Internals::Registry.new } it "accepts a private actor registry" do expect(registry[:example]).to be_running end end it "removes actors from the registry when terminating" do subject.terminate expect(Celluloid::Actor[:example]).to be_nil end it "allows external access to the internal registry" do expect(subject[:example]).to be_a MyContainerActor end end context "with multiple args" do subject do Class.new(Celluloid::Supervision::Container) do supervise type: MyContainerActor, as: :example, args: %i[foo bar] end.run! end it "passes them" do expect(Celluloid::Actor[:example].args).to eq(%i[foo bar]) end end context "with lazy evaluation" do subject do Class.new(Celluloid::Supervision::Container) do supervise type: MyContainerActor, as: :example, args: -> { :lazy } end.run! end it "evaluates correctly" do expect(Celluloid::Actor[:example].args).to eq([:lazy]) end end xit("can remove members") do end end celluloid-0.18.0/spec/celluloid/supervision/instance_spec.rb0000644000004100000410000000037114005764144024251 0ustar www-datawww-data# RSpec.describe Celluloid::Supervision::Configuration do # xit("can be instantiated without a name") { # } # # xit("can be instantiated with a name") { # } # # context("can be terminated") { # xit("cleanly") { # } # } # end # = celluloid-0.18.0/spec/celluloid/supervision/configuration_spec.rb0000644000004100000410000001347314005764144025323 0ustar www-datawww-dataRSpec.describe Celluloid::Supervision::Configuration, actor_system: :global do class TestActor include Celluloid def identity :testing end end let(:succeeding) do { as: :testing, type: TestActor } end let(:failing) do { as: :testing, type: TestActor, args: [:fail] } end after(:each) do Celluloid::Supervision::Configuration.resync_parameters subject.resync_accessors end context("remains reusable without being mutated") do it("properly") do expect(Celluloid.actor_system.root_configuration.export).to eq(Celluloid::Actor::System::ROOT_SERVICES) end end context("metaprogramming") do context("Celluloid.services accessor") do it("is dynamically added, and available") do expect(Celluloid.services.respond_to?(:supervise)).to be_truthy end it("allows supervision") do Celluloid.services.supervise(type: TestActor, as: :test_actor) expect(Celluloid.services.test_actor.identity).to eq(:testing) end end context("supervised actors can create accessors") do it("which are dynamically added, and available as Celluloid.accessor") do TestActor.supervise(as: :test_actor, accessors: [:test_actor]) expect(Celluloid.test_actor.identity).to eq(:testing) end end end context("parameters") do context("can be added to") do context("can be given new :mandatory parameters") do before(:each) do Celluloid::Supervision::Configuration.parameter! :mandatory, :special_requirement subject.resync_accessors end it("programmatically") do expect(Celluloid::Supervision::Configuration.parameters(:mandatory)).to include(:special_requirement) end it("and respond appropriately") do subject.resync_accessors expect(subject.methods).to include(:special_requirement) expect(subject.respond_to?(:special_requirement!)).to be_truthy expect(subject.respond_to?(:special_requirement?)).to be_truthy expect(subject.respond_to?(:special_requirement=)).to be_truthy expect(subject.respond_to?(:special_requirement)).to be_truthy end it("and instances will respond appropriately") do subject.instances.first.resync_accessors subject.define(type: TestActor, special_requirement: :valid) expect(subject.respond_to?(:special_requirement)).to be_truthy end it("and be reset to defaults") do Celluloid::Supervision::Configuration.resync_parameters expect(Celluloid::Supervision::Configuration.parameters(:mandatory)).not_to include(:special_requirement) end end context("can be aliased") do before(:each) do Celluloid::Supervision::Configuration.resync_parameters Celluloid::Supervision::Configuration.alias! :nick, :as subject.resync_accessors end it("programmatically") do expect(Celluloid::Supervision::Configuration.aliases.keys).to include(:nick) end it("and respond appropriately by method") do subject.define(type: TestActor, as: :test_name) expect(subject.respond_to?(:nick!)).to be_truthy expect(subject.respond_to?(:nick?)).to be_truthy expect(subject.respond_to?(:nick=)).to be_truthy expect(subject.respond_to?(:nick)).to be_truthy expect(subject.nick).to eq(:test_name) end xit("and respond properly by current_instance, by method") do # subject.current_instance[:aliased] gets subject.current_instance[:original] end it("and instances will respond properly by method") do subject.define(as: :test_name, type: TestActor) expect(subject.instances.first.respond_to?(:nick!)).to be_truthy expect(subject.instances.first.respond_to?(:nick?)).to be_truthy expect(subject.instances.first.respond_to?(:nick=)).to be_truthy expect(subject.instances.first.respond_to?(:nick)).to be_truthy expect(subject.instances.first.nick).to eq(:test_name) end xit("and respond appropriately by key") do # subject[:aliased] gets subject[:original] end xit("and instances respond properly by current_instance, by key") do # subject.instances.first[:aliased] gets subject.instances.first[:original] end xit("and instances respond properly by key") do # subject.instances.first[:aliased] gets subject.instances.first[:original] end end end end context("Configuration.define class method") do xit("can take individual instance configuration") do end xit("can take array of instance configurations") do end end context("Configuration#define instance method") do xit("can take individual instance configuration") do end xit("can take array of instance configurations") do end end context("Configuration.deploy class method") do xit("can take individual instance configuration") do end xit("can take array of instance configurations") do end end context("Configuration#deploy instance method") do xit("can take individual instance configuration") do end xit("can take array of instance configurations") do end end context("accessing information") do before(:each) { subject.define(succeeding) } it("can get values out of current level of configuration by [:key]") do expect(subject[:as]).to eq(:testing) end it("can get values out of current level of configuration by #key") do expect(subject.as).to eq(:testing) end end it("verifies arity of intended actor's initialize method") do expect { subject.define(failing) }.to raise_exception(ArgumentError) end end celluloid-0.18.0/spec/celluloid/supervision/behavior_spec.rb0000644000004100000410000000320514005764144024243 0ustar www-datawww-dataRSpec.describe Celluloid::Supervision::Container::Behavior do class WellBehaved include Celluloid end let(:typeless) do { as: :testing_behaviors, supervises: [ { as: :testing_behaved_instances, type: WellBehaved } ] } end let(:normal) do { as: :testing_behaviors, supervises: [ { as: :testing_behaved_instances, type: WellBehaved } ] } end let(:mutant) do { as: :testing_behaviors, supervise: [], supervises: [ { as: :testing_behaved_instances, type: WellBehaved } ] } end subject { Celluloid::Supervision::Configuration.new } it("detects default type if not provided, and provides it") do end it("prejudicially rejects mutants") do expect { subject.define(mutant) }.to raise_error(Celluloid::Supervision::Container::Behavior::Error::Mutant) end context("allows definition of new plugins") do class TestPlugin include Celluloid::Supervision::Container::Behavior identifier! :identifying_parameter, :aliased_identifier end xit("and adds a plugin parameter") do expect(Celluloid::Supervision::Configuration.parameters(:plugins)).to include(:identifying_parameter) end xit("and adds aliased parameters") do expect(Celluloid::Supervision::Configuration.aliases.keys?).to include(:aliased_identifier) end end context("allows inclusion by Module") do xit("with automatic addition of injections") do end xit("with correct handling of injections made") do end end end celluloid-0.18.0/spec/celluloid/supervision/supervisor_spec.rb0000644000004100000410000000576614005764144024703 0ustar www-datawww-dataRSpec.describe "Celluloid supervisor", actor_system: :global do let(:logger) { Specs::FakeLogger.current } it "restarts actors when they die" do supervisor = Celluloid.supervise(type: Subordinate, args: [:idle]) subordinate = supervisor.actors.first expect(subordinate.state).to be(:idle) subordinate.crack_the_whip expect(subordinate.state).to be(:working) allow(logger).to receive(:crash).with("Actor crashed!", SubordinateDead) expect do subordinate.crack_the_whip end.to raise_exception(SubordinateDead) sleep 0.1 # hax to prevent race :( expect(subordinate).not_to be_alive new_subordinate = supervisor.actors.first expect(new_subordinate).not_to eq subordinate expect(new_subordinate.state).to eq :idle end it "registers actors and reregisters them when they die" do Celluloid.supervise(as: :subordinate, type: Subordinate, args: [:idle]) subordinate = Celluloid::Actor[:subordinate] expect(subordinate.state).to be(:idle) subordinate.crack_the_whip expect(subordinate.state).to be(:working) allow(logger).to receive(:crash).with("Actor crashed!", SubordinateDead) expect do subordinate.crack_the_whip end.to raise_exception(SubordinateDead) sleep 0.1 # hax to prevent race :( expect(subordinate).not_to be_alive new_subordinate = Celluloid::Actor[:subordinate] expect(new_subordinate).not_to eq subordinate expect(new_subordinate.state).to eq :idle end it "creates supervisors via Actor.supervise" do supervisor = Subordinate.supervise(args: [:working]) subordinate = supervisor.actors.first expect(subordinate.state).to be(:working) allow(logger).to receive(:crash).with("Actor crashed!", SubordinateDead) expect do subordinate.crack_the_whip end.to raise_exception(SubordinateDead, "the spec purposely crashed me :(") sleep 0.1 # hax to prevent race :( expect(subordinate).not_to be_alive new_subordinate = supervisor.actors.first expect(new_subordinate).not_to eq subordinate expect(new_subordinate.state).to eq :working end it "creates supervisors and registers actors via Actor.supervise as:" do supervisor = Subordinate.supervise(as: :subordinate, args: [:working]) subordinate = Celluloid::Actor[:subordinate] expect(subordinate.state).to be(:working) allow(logger).to receive(:crash).with("Actor crashed!", SubordinateDead) expect do subordinate.crack_the_whip end.to raise_exception(SubordinateDead) sleep 0.1 # hax to prevent race :( expect(subordinate).not_to be_alive new_subordinate = supervisor.actors.first expect(new_subordinate).not_to eq subordinate expect(new_subordinate.state).to be(:working) end it "removes an actor if it terminates cleanly" do supervisor = Subordinate.supervise(args: [:working]) subordinate = supervisor.actors.first expect(supervisor.actors).to eq([subordinate]) subordinate.terminate expect(supervisor.actors).to be_empty end end celluloid-0.18.0/spec/celluloid/supervision/root_spec.rb0000644000004100000410000000137714005764144023437 0ustar www-datawww-dataRSpec.describe Celluloid::Supervision::Service::Root, actor_system: :global do class RootTestingActor include Celluloid def identity :testing end end before(:all) do Celluloid::Supervision::Configuration.resync_parameters end context("deploys root services") do it("properly") do expect(Celluloid.actor_system.registered).to eq(Celluloid::Actor::System::ROOT_SERVICES.map { |r| r[:as] }) end end context("makes public services available") do it("properly") do expect(Celluloid.services.respond_to?(:supervise)).to be_truthy end it("and accepts one-off actor supervision") do RootTestingActor.supervise as: :tester expect(Celluloid[:tester].identity).to eq(:testing) end end end celluloid-0.18.0/spec/celluloid/actor_spec.rb0000644000004100000410000000015014005764144021162 0ustar www-datawww-datarequire "spec_helper" RSpec.describe Celluloid do it_behaves_like "a Celluloid Actor", Celluloid end celluloid-0.18.0/spec/celluloid/misc/0000755000004100000410000000000014005764144017452 5ustar www-datawww-datacelluloid-0.18.0/spec/celluloid/misc/leak_spec.rb0000644000004100000410000000265714005764144021737 0ustar www-datawww-datarequire "weakref" RSpec.describe "Leaks", actor_system: :global, leaktest: true, skip: !ENV["CELLULOID_LEAKTEST"] && "leak test disabled" do class LeakActor include Celluloid def initialize(arg) @arg = arg end def call(_arg) [] end def terminate; end end def wait_for_release(weak, _what, count = 1000) trash = [] count.times do |step| GC.start return true unless weak.weakref_alive? trash << "*" * step end false end def do_actor(what) actor = LeakActor.new what weak = yield actor actor.terminate actor = nil weak end def actor_life(what, &block) GC.start weak = do_actor what, &block expect(wait_for_release(weak, what)).to be_truthy end context "celluloid actor" do xit "is properly destroyed upon termination" do actor_life("actor") do |actor| WeakRef.new actor end end end context "celluloid actor call" do it "does not hold a reference its arguments on completion" do actor_life("call-arg") do |actor| arg = [] actor.call arg weak = WeakRef.new arg arg = nil weak end end it "does not hold a reference to the return value" do actor_life("call-result") do |actor| result = actor.call nil weak = WeakRef.new result result = nil weak end end end end celluloid-0.18.0/spec/celluloid/logging/0000755000004100000410000000000014005764144020145 5ustar www-datawww-datacelluloid-0.18.0/spec/celluloid/logging/ring_buffer_spec.rb0000644000004100000410000000152014005764144023772 0ustar www-datawww-dataRSpec.describe Celluloid::RingBuffer do subject { Celluloid::RingBuffer.new(2) } it { is_expected.to be_empty } it { is_expected.not_to be_full } it "should push and shift" do subject.push("foo") subject.push("foo2") expect(subject.shift).to eq("foo") expect(subject.shift).to eq("foo2") end it "should push past the end" do subject.push("foo") subject.push("foo2") subject.push("foo3") expect(subject).to be_full end it "should shift the most recent" do (1..5).each { |i| subject.push(i) } expect(subject.shift).to be 4 expect(subject.shift).to be 5 expect(subject.shift).to be_nil end it "should return nil when shifting empty" do expect(subject).to be_empty expect(subject.shift).to be_nil end it "should be thread-safe" do # TODO: how to test? end end celluloid-0.18.0/spec/celluloid/group/0000755000004100000410000000000014005764144017653 5ustar www-datawww-datacelluloid-0.18.0/spec/celluloid/group/spawner_spec.rb0000644000004100000410000000065414005764144022676 0ustar www-datawww-data# set logger early on require "celluloid/internals/logger" if Celluloid.group_class == Celluloid::Group::Spawner RSpec.describe Celluloid::Group::Spawner do it_behaves_like "a Celluloid Group" it "does not leak finished threads" do queue = Queue.new th = subject.get { queue.pop } expect do queue << nil th.join end.to change { subject.group.length }.by(-1) end end end celluloid-0.18.0/spec/celluloid/group/pool_spec.rb0000644000004100000410000000031414005764144022161 0ustar www-datawww-data# set logger early on require "celluloid/internals/logger" if Celluloid.group_class == Celluloid::Group::Pool RSpec.describe Celluloid::Group::Pool do it_behaves_like "a Celluloid Group" end end celluloid-0.18.0/spec/celluloid/group/elastic_spec.rb0000644000004100000410000000000014005764144022624 0ustar www-datawww-datacelluloid-0.18.0/spec/celluloid/task/0000755000004100000410000000000014005764144017461 5ustar www-datawww-datacelluloid-0.18.0/spec/celluloid/task/fibered_spec.rb0000644000004100000410000000023714005764144022422 0ustar www-datawww-dataRSpec.describe Celluloid::Task::Fibered, actor_system: :within do it_behaves_like "a Celluloid Task" if Celluloid.task_class == Celluloid::Task::Fibered end celluloid-0.18.0/spec/celluloid/task/threaded_spec.rb0000644000004100000410000000024114005764144022575 0ustar www-datawww-dataRSpec.describe Celluloid::Task::Threaded, actor_system: :within do it_behaves_like "a Celluloid Task" if Celluloid.task_class == Celluloid::Task::Threaded end celluloid-0.18.0/spec/celluloid/block_spec.rb0000644000004100000410000000544014005764144021153 0ustar www-datawww-dataRSpec.describe "Blocks", actor_system: :global do class MyBlockActor include Celluloid def initialize(name, data) @name = name @data = data end attr_reader :name def ask_for_something(other) sender_actor = current_actor @data << [:outside, @name, current_actor.name] other.do_something_and_callback do |_value| @data << [:yielded, @name, current_actor.name] @data << receive_result(:self) @data << current_actor.receive_result(:current_actor) @data << sender_actor.receive_result(:sender) :pete_the_polyglot_alien end end def deferred_excecution(value, &_block) defer do yield(value) end end def deferred_current_actor(&_block) defer do yield(current_actor.name) end end def defer_for_something(other, &_block) sender_actor = current_actor defer do @data << [:outside, @name, current_actor.name] other.do_something_and_callback do |_value| @data << [:yielded, @name, current_actor.name] @data << receive_result(:self) @data << current_actor.receive_result(:current_actor) @data << sender_actor.receive_result(:sender) :pete_the_polyglot_alien end end end def do_something_and_callback @data << [:something, @name, current_actor.name] @data << yield(:foo) end def receive_result(result) [result, @name, current_actor.name] end end it "work between actors" do data = [] a1 = MyBlockActor.new("one", data) a2 = MyBlockActor.new("two", data) a1.ask_for_something a2 expected = [ [:outside, "one", "one"], [:something, "two", "two"], [:yielded, "one", "one"], [:self, "one", "one"], [:current_actor, "one", "one"], [:sender, "one", "one"], :pete_the_polyglot_alien ] expect(data).to eq(expected) end execute_deferred = proc do a1 = MyBlockActor.new("one", []) expect(a1.deferred_excecution(:pete_the_polyglot_alien) { |v| v }) .to eq(:pete_the_polyglot_alien) end xit("can be deferred", &execute_deferred) xit "can execute deferred blocks referencing current_actor" do a1 = MyBlockActor.new("one", []) expect(a1.deferred_current_actor { |v| v }).to be("one") end xit "can execute deferred blocks with another actor" do data = [] a1 = MyBlockActor.new("one", data) a2 = MyBlockActor.new("two", data) a1.defer_for_something a2 expected = [ [:outside, "one", "one"], [:something, "two", "two"], [:yielded, "one", "one"], [:self, "one", "one"], [:current_actor, "one", "one"], [:sender, "one", "one"], :pete_the_polyglot_alien ] expect(data).to eq(expected) end end celluloid-0.18.0/spec/celluloid/timer_spec.rb0000644000004100000410000000141714005764144021201 0ustar www-datawww-dataclass EveryActor include Celluloid def initialize @trace = [] @times = [] @start = Time.now every(1) { log(1) } every(2) { log(2) } every(1) { log(11) } every(2) { log(22) } end def log(t) @trace << t offset = Time.now - @start @times << offset # puts "log(#{t}) @ #{offset}" end attr_reader :trace attr_reader :times end RSpec.describe "Celluloid::Actor timers" do it "run every(t) task several times" do Celluloid.boot every_actor = EveryActor.new sleep 5.5 every_actor.times trace = every_actor.trace Celluloid.shutdown expect(trace.count(1)).to be == 5 expect(trace.count(11)).to be == 5 expect(trace.count(2)).to be == 2 expect(trace.count(22)).to be == 2 end end celluloid-0.18.0/spec/celluloid/future_spec.rb0000644000004100000410000000236514005764144021376 0ustar www-datawww-dataRSpec.describe Celluloid::Future, actor_system: :global do it "creates future objects that can be retrieved later" do future = Celluloid::Future.new { 40 + 2 } expect(future.value).to eq(42) end it "passes arguments to future blocks" do future = Celluloid::Future.new(40) { |n| n + 2 } expect(future.value).to eq(42) end it "reraises exceptions that occur when the value is retrieved" do class ExampleError < StandardError; end future = Celluloid::Future.new { raise ExampleError, "oh noes crash!" } expect { future.value }.to raise_exception(ExampleError) end it "knows if it's got a value yet" do queue = Queue.new future = Celluloid::Future.new { queue.pop } expect(future).not_to be_ready queue << nil # let it continue Specs.sleep_and_wait_until { future.ready? } expect(future).to be_ready end it "raises TaskTimeout when the future times out" do future = Celluloid::Future.new { sleep 2 } expect { future.value(1) }.to raise_exception(Celluloid::TaskTimeout) end it "cancels future" do future = Celluloid::Future.new { sleep 3600 } future.cancel(StandardError.new("cancelled")) expect { future.value }.to raise_exception(StandardError, "cancelled") end end celluloid-0.18.0/spec/celluloid/actor/0000755000004100000410000000000014005764144017627 5ustar www-datawww-datacelluloid-0.18.0/spec/celluloid/actor/system_spec.rb0000644000004100000410000000352114005764144022513 0ustar www-datawww-dataRSpec.describe Celluloid::Actor::System do class TestActor include Celluloid def identity :testing end end after do subject.shutdown end it "supports non-global Actor::System" do subject.within do expect(Celluloid.actor_system).to eq(subject) end end it "makes actors accessible by Celluloid[:actor]" do subject.start subject.within do TestActor.supervise as: :testing, type: TestActor expect(subject.registered).to include(:testing) expect(Celluloid::Actor[:testing].identity).to eq(:testing) end end it "starts default actors" do subject.start expect(subject.registered).to eq(Celluloid::Actor::System::ROOT_SERVICES.map { |r| r[:as] }) end it "support getting threads" do queue = Queue.new subject.get_thread do expect(Celluloid.actor_system).to eq(subject) queue << nil end queue.pop end it "allows a stack dump" do expect(subject.stack_dump).to be_a(Celluloid::Internals::Stack::Dump) end it "allows a stack summary" do expect(subject.stack_summary).to be_a(Celluloid::Internals::Stack::Summary) end it "returns named actors" do subject.start subject.within do TestActor.supervise as: :test end expect(subject.registered).to include(:test) end it "returns running actors" do expect(subject.running).to be_empty first = subject.within do TestActor.new end second = subject.within do TestActor.new end expect(subject.running).to eq([first, second]) end it "shuts down" do subject.shutdown expect { subject.get_thread } .to raise_error(Celluloid::NotActive) end it "warns nicely when no actor system is started" do expect { TestActor.new } .to raise_error("Celluloid is not yet started; use Celluloid.boot") end end celluloid-0.18.0/spec/celluloid/condition_spec.rb0000644000004100000410000000413214005764144022044 0ustar www-datawww-dataRSpec.describe Celluloid::Condition, actor_system: :global do let(:logger) { Specs::FakeLogger.current } class ConditionExample include Celluloid attr_reader :condition, :signaled_times def initialize @condition = Condition.new @waiting = false @signaled_times = 0 end def signal_condition(condition, value) condition.signal value end def wait_for_condition(timeout = nil) @waiting = true begin value = @condition.wait(timeout) @signaled_times += 1 ensure @waiting = false end value end def waiting? @waiting end end let(:actor) { ConditionExample.new } after { actor.terminate rescue nil } it "sends signals" do 3.times { actor.async.wait_for_condition } expect(actor.signaled_times).to be_zero actor.condition.signal expect(actor.signaled_times).to be(1) end it "broadcasts signals" do 3.times { actor.async.wait_for_condition } expect(actor.signaled_times).to be_zero actor.condition.broadcast expect(actor.signaled_times).to be(3) end it "sends values along with signals" do future = actor.future(:wait_for_condition) actor.condition.signal(:example_value) expect(future.value).to be(:example_value) end it "supports running blocks with value once obtained" do condition = Celluloid::Condition.new actor.async.signal_condition condition, :value expect(condition.wait { |value| "#{value} post-handled" }).to eq("value post-handled") end it "supports waiting outside actors" do condition = Celluloid::Condition.new actor.async.signal_condition condition, :value expect(condition.wait).to eq(:value) end it "times out inside normal Threads" do condition = Celluloid::Condition.new expect { condition.wait(1) } .to raise_error(Celluloid::ConditionError) end it "times out inside Tasks" do allow(logger).to receive(:crash).with("Actor crashed!", Celluloid::ConditionError) expect { actor.wait_for_condition(1) } .to raise_error(Celluloid::ConditionError) end end celluloid-0.18.0/spec/celluloid/mailbox/0000755000004100000410000000000014005764144020152 5ustar www-datawww-datacelluloid-0.18.0/spec/celluloid/mailbox/evented_spec.rb0000644000004100000410000000164514005764144023151 0ustar www-datawww-dataRSpec.describe Celluloid::Mailbox::Evented do subject { TestEventedMailbox.new } it_behaves_like "a Celluloid Mailbox" it "recovers from timeout exceeded to process mailbox message" do timeout_interval = Specs::TIMER_QUANTUM + 0.1 started_at = Time.now expect do ::Timeout.timeout(timeout_interval) do subject.receive { false } end end.to raise_exception(Timeout::Error) expect(Time.now - started_at).to be_within(Specs::TIMER_QUANTUM).of timeout_interval end it "discard messages when reactor wakeup fails" do expect(Celluloid::Internals::Logger).to receive(:crash).with("reactor crashed", RuntimeError) expect(Celluloid.logger).to receive(:debug).with("Discarded message (mailbox is dead): first") bad_reactor = Class.new do def wakeup raise end end mailbox = Celluloid::Mailbox::Evented.new(bad_reactor) mailbox << :first end end celluloid-0.18.0/spec/celluloid/notifications_spec.rb0000644000004100000410000000665514005764144022743 0ustar www-datawww-dataRSpec.describe Celluloid::Notifications, actor_system: :global do class Admirer include Celluloid include Celluloid::Notifications attr_reader :mourning attr_reader :mourning_count def someone_died(_topic, name) @mourning = name @mourning_count ||= 0 @mourning_count += 1 end end class President include Celluloid include Celluloid::Notifications def die(topic = "death") publish(topic, "Mr. President") end end it "notifies relevant subscribers" do marilyn = Admirer.new jackie = Admirer.new marilyn.subscribe("death", :someone_died) jackie.subscribe("alive", :someone_died) president = President.new president.die expect(marilyn.mourning).to eq("Mr. President") expect(jackie.mourning).not_to eq("Mr. President") end it "allows multiple subscriptions from the same actor" do marilyn = Admirer.new marilyn.subscribe("death", :someone_died) marilyn.subscribe("death", :someone_died) president = President.new president.die expect(marilyn.mourning_count).to be(2) end it "notifies subscribers" do marilyn = Admirer.new jackie = Admirer.new marilyn.subscribe("death", :someone_died) jackie.subscribe("death", :someone_died) president = President.new president.die expect(marilyn.mourning).to eq("Mr. President") expect(jackie.mourning).to eq("Mr. President") end it "publishes even if there are no subscribers" do president = President.new president.die end it "allows symbol subscriptions" do marilyn = Admirer.new jackie = Admirer.new marilyn.subscribe(:death, :someone_died) jackie.subscribe("death", :someone_died) president = President.new president.die(:death) expect(marilyn.mourning).to eq("Mr. President") expect(jackie.mourning).to eq("Mr. President") end it "allows regex subscriptions" do marilyn = Admirer.new marilyn.subscribe(/(death|assassination)/, :someone_died) president = President.new president.die expect(marilyn.mourning).to eq("Mr. President") end it "matches symbols against regex subscriptions" do marilyn = Admirer.new marilyn.subscribe(/(death|assassination)/, :someone_died) president = President.new president.die(:assassination) expect(marilyn.mourning).to eq("Mr. President") end it "allows unsubscribing" do marilyn = Admirer.new subscription = marilyn.subscribe("death", :someone_died) marilyn.unsubscribe(subscription) president = President.new president.die expect(marilyn.mourning).to be_nil end it "prunes dead subscriptions" do marilyn = Admirer.new jackie = Admirer.new marilyn.subscribe("death", :someone_died) jackie.subscribe("death", :someone_died) listeners = Celluloid::Notifications.notifier.listeners_for("death").size marilyn.terminate after_listeners = Celluloid::Notifications.notifier.listeners_for("death").size expect(after_listeners).to eq(listeners - 1) end it "prunes multiple subscriptions from a dead actor" do marilyn = Admirer.new marilyn.subscribe("death", :someone_died) marilyn.subscribe("death", :someone_died) listeners = Celluloid::Notifications.notifier.listeners_for("death").size marilyn.terminate after_listeners = Celluloid::Notifications.notifier.listeners_for("death").size expect(after_listeners).to eq(listeners - 2) end end celluloid-0.18.0/spec/celluloid/evented_mailbox_spec.rb0000644000004100000410000000017714005764144023230 0ustar www-datawww-dataRSpec.describe Celluloid::Mailbox::Evented do subject { TestEventedMailbox.new } it_behaves_like "a Celluloid Mailbox" end celluloid-0.18.0/spec/celluloid/proxy_spec.rb0000644000004100000410000000162414005764144021242 0ustar www-datawww-dataRSpec.describe Celluloid::Proxy::Abstract do around do |ex| Celluloid.boot ex.run Celluloid.shutdown end let(:task_klass) { Celluloid.task_class } let(:actor_class) { ExampleActorClass.create(CelluloidSpecs.included_module, task_klass) } let(:actor) { actor_class.new "Troy McClure" } let(:logger) { Specs::FakeLogger.current } it "should be eql? to self" do expect(actor.eql?(actor)).to be_truthy end it "should be eql? to self even if dead" do actor.terminate expect(actor.eql?(actor)).to be_truthy end it "should not be eql? to other proxy objects" do other_future = Celluloid::Proxy::Future.new(actor.mailbox, actor.__klass__) expect(actor.future.eql?(other_future)).to be_truthy end it "should be possible to compare with non-proxy objects" do expect(actor.eql?("string")).to be_falsey expect("string".eql?(actor)).to be_falsey end end celluloid-0.18.0/spec/celluloid/calls_spec.rb0000644000004100000410000000223414005764144021155 0ustar www-datawww-dataRSpec.describe Celluloid::Call::Sync, actor_system: :global do # TODO: these should be Call::Sync unit tests (without working on actual actors) let(:actor) { CallExampleActor.new } let(:logger) { Specs::FakeLogger.current } context "when obj does not respond to a method" do it "raises a NoMethodError" do allow(logger).to receive(:crash).with("Actor crashed!", NoMethodError) expect do actor.the_method_that_was_not_there end.to raise_exception(NoMethodError) end context "when obj raises during inspect" do it "should emulate obj.inspect" do allow(logger).to receive(:crash).with("Actor crashed!", NoMethodError) end end end it "aborts with ArgumentError when a method is called with too many arguments" do allow(logger).to receive(:crash).with("Actor crashed!", ArgumentError) expect do actor.actual_method("with too many arguments") end.to raise_exception(ArgumentError) end it "preserves call chains across synchronous calls" do actor2 = CallExampleActor.new(actor) uuid, next_actor_uuid = actor2.chained_call_ids expect(uuid).to eq next_actor_uuid end end celluloid-0.18.0/spec/celluloid/internals/0000755000004100000410000000000014005764144020516 5ustar www-datawww-datacelluloid-0.18.0/spec/celluloid/internals/thread_handle_spec.rb0000644000004100000410000000302514005764144024637 0ustar www-datawww-dataRSpec.describe Celluloid::Internals::ThreadHandle do let(:actor_system) { Celluloid::Actor::System.new } after { actor_system.shutdown } context "given a living thread" do let(:args) { [actor_system] } before do @thread = nil @thread_info_queue = Queue.new @handle = Celluloid::Internals::ThreadHandle.new(*args) do @thread_info_queue << Thread.current sleep end @thread = Timeout.timeout(2) { @thread_info_queue.pop } end it "knows the thread is alive" do alive = @handle.alive? if @thread @thread.kill @thread.join else STDERR.puts "NOTE: something failed - thread missing" end expect(alive).to be(true) end context "when a role is provided" do let(:args) { [actor_system, :useful] } it "can be retrieved from thread directly" do role = @thread.role if @thread @thread.kill @thread.join else STDERR.puts "NOTE: something failed - thread missing" end expect(role).to eq(:useful) end end end context "given a finished thread" do before do thread_info_queue = Queue.new @handle = Celluloid::Internals::ThreadHandle.new(actor_system) do thread_info_queue << Thread.current end thread = thread_info_queue.pop thread.kill Specs.sleep_and_wait_until { !thread.alive? } end it "knows the thread is no longer alive" do expect(@handle).not_to be_alive end end end celluloid-0.18.0/spec/celluloid/internals/stack/0000755000004100000410000000000014005764144021623 5ustar www-datawww-datacelluloid-0.18.0/spec/celluloid/internals/stack/summary_spec.rb0000644000004100000410000000021214005764144024652 0ustar www-datawww-dataRSpec.describe Celluloid::Internals::Stack::Summary do subject { actor_system.stack_summary } it_behaves_like "a Celluloid Stack" end celluloid-0.18.0/spec/celluloid/internals/stack/dump_spec.rb0000644000004100000410000000020414005764144024123 0ustar www-datawww-dataRSpec.describe Celluloid::Internals::Stack::Dump do subject { actor_system.stack_dump } it_behaves_like "a Celluloid Stack" end celluloid-0.18.0/spec/celluloid/internals/links_spec.rb0000644000004100000410000000211514005764144023174 0ustar www-datawww-dataRSpec.describe Celluloid::Internals::Links do subject { Celluloid::Internals::Links.new } let(:mailbox_mock) do Class.new(Array) do attr_reader :address def initialize(address) @address = address end end end let(:first_actor) do Struct.new(:mailbox).new(mailbox_mock.new("foo123")) end let(:second_actor) do Struct.new(:mailbox).new(mailbox_mock.new("bar456")) end it "is Enumerable" do expect(subject).to be_an(Enumerable) end it "adds actors by their mailbox address" do expect(subject.include?(first_actor)).to be_falsey subject << first_actor expect(subject.include?(first_actor)).to be_truthy end it "removes actors by their mailbox address" do subject << first_actor expect(subject.include?(first_actor)).to be_truthy subject.delete first_actor expect(subject.include?(first_actor)).to be_falsey end it "iterates over all actors" do subject << first_actor subject << second_actor expect(subject.inject([]) { |all, a| all << a }).to eq([first_actor, second_actor]) end end celluloid-0.18.0/spec/celluloid/internals/cpu_counter_spec.rb0000644000004100000410000000735714005764144024417 0ustar www-datawww-dataRSpec.describe Celluloid::Internals::CPUCounter do describe "#cores" do subject { described_class.cores } let(:num_cores) { 1024 } before do allow(described_class).to receive(:`) { raise "backtick stub called" } allow(::IO).to receive(:open).and_raise("IO.open stub called!") described_class.instance_variable_set(:@cores, nil) end after do ENV["NUMBER_OF_PROCESSORS"] = nil described_class.instance_variable_set(:@cores, nil) end context "with from_env" do context "valid env value" do before { ENV["NUMBER_OF_PROCESSORS"] = num_cores.to_s } it { is_expected.to eq num_cores } end context "invalid env value" do before { ENV["NUMBER_OF_PROCESSORS"] = "" } subject { described_class.from_env } it { is_expected.to be nil } end end context "with from_sysdev" do subject { described_class.from_sysdev } context "when /sys/devices/system/cpu/present exists" do before do expect(::IO).to receive(:read).with("/sys/devices/system/cpu/present") .and_return("dunno-whatever-#{num_cores - 1}") end it { is_expected.to eq num_cores } end context "when /sys/devices/system/cpu/present does NOT exist" do before do expect(::IO).to receive(:read).with("/sys/devices/system/cpu/present") .and_raise(Errno::ENOENT) end context "when /sys/devices/system/cpu/cpu* files exist" do before do cpu_entries = (1..num_cores).map { |n| "cpu#{n}" } cpu_entries << "non-cpu-entry-to-ignore" expect(Dir).to receive(:[]).with("/sys/devices/system/cpu/cpu*") .and_return(cpu_entries) end it { is_expected.to eq num_cores } end end context "when /sys/devices/system/cpu/cpu* files DO NOT exist" do before do expect(Dir).to receive(:[]).with("/sys/devices/system/cpu/cpu*") .and_return([]) end end end context "with from_java" do subject { described_class.from_java } xit "not yet tested" do end end context "with from_proc" do subject { described_class.from_proc } xit "not yet tested" do end end context "with from_win32ole" do subject { described_class.from_win32ole } xit "not yet tested" do end end context "with from_sysctl" do subject { described_class.from_sysctl } context "when sysctl blows up" do before { allow(described_class).to receive(:`).and_raise(Errno::EINTR) } it { is_expected.to be nil } end context "when sysctl fails" do before { allow(described_class).to receive(:`).and_return(`false`) } it { is_expected.to be nil } end context "when sysctl succeeds" do before do expect(described_class).to receive(:`).with("sysctl -n hw.ncpu 2>/dev/null") .and_return(num_cores.to_s) `true` end it { is_expected.to eq num_cores } end end xit "when all guesses fail" do end context "with from_result" do context "when passed a symbol" do subject { described_class.from_result(:foo) } it { is_expected.to be nil } end context "when passed 0" do subject { described_class.from_result(:foo) } it { is_expected.to be nil } end context "when passed valid integer" do subject { described_class.from_result(num_cores) } it { is_expected.to be num_cores } end end end end celluloid-0.18.0/spec/celluloid/internals/registry_spec.rb0000644000004100000410000000346514005764144023735 0ustar www-datawww-dataRSpec.describe Celluloid::Internals::Registry, actor_system: :global do class Marilyn include Celluloid def sing_for(person) "o/~ Happy birthday, #{person}" end end it "registers Actors" do Celluloid::Actor[:marilyn] = Marilyn.new expect(Celluloid::Actor[:marilyn].sing_for("Mr. President")).to eq("o/~ Happy birthday, Mr. President") end it "refuses to register non-Actors" do expect do Celluloid::Actor[:impostor] = Object.new end.to raise_error TypeError end it "lists all registered actors" do Celluloid::Actor[:marilyn] = Marilyn.new expect(Celluloid::Actor.registered).to include :marilyn end it "knows its name once registered" do Celluloid::Actor[:marilyn] = Marilyn.new expect(Celluloid::Actor[:marilyn].registered_name).to eq(:marilyn) end describe :delete do before do Celluloid::Actor[:marilyn] ||= Marilyn.new end it "removes reference to actors' name from the registry" do Celluloid::Actor.delete(:marilyn) expect(Celluloid::Actor.registered).not_to include :marilyn end it "returns actor removed from the registry" do rval = Celluloid::Actor.delete(:marilyn) expect(rval).to be_kind_of(Marilyn) end end describe :clear do it "should return a hash of registered actors and remove them from the registry" do Celluloid::Actor[:marilyn] ||= Marilyn.new rval = Celluloid::Actor.clear_registry begin expect(rval).to be_kind_of(Hash) expect(rval).to have_key(:marilyn) expect(rval[:marilyn].wrapped_object).to be_instance_of(Marilyn) expect(Celluloid::Actor.registered).to be_empty ensure # Repopulate the registry once we're done rval.each { |key, actor| Celluloid::Actor[key] = actor } end end end end celluloid-0.18.0/spec/celluloid/internals/properties_spec.rb0000644000004100000410000000217414005764144024255 0ustar www-datawww-dataRSpec.describe Celluloid::Internals::Properties do let(:default_value) { 42 } let(:changed_value) { 43 } let(:example_class) do Class.new do extend Celluloid::Internals::Properties property :baz, default: 42 end end let(:example_subclass) do Class.new(example_class) end let(:example_subclass_subclass) do Class.new(example_subclass) end it "adds properties to classes" do expect(example_class.baz).to eq default_value example_class.baz changed_value expect(example_class.baz).to eq changed_value end it "allows properties to be inherited" do expect(example_subclass.baz).to eq default_value example_subclass.baz changed_value expect(example_subclass.baz).to eq changed_value expect(example_class.baz).to eq default_value end it "allows properties to be deeply inherited" do expect(example_subclass_subclass.baz).to eq default_value example_subclass_subclass.baz changed_value expect(example_subclass_subclass.baz).to eq changed_value expect(example_subclass.baz).to eq default_value expect(example_class.baz).to eq default_value end end celluloid-0.18.0/spec/celluloid/internals/uuid_spec.rb0000644000004100000410000000043614005764144023026 0ustar www-datawww-dataRSpec.describe Celluloid::Internals::UUID do U = Celluloid::Internals::UUID it "generates unique IDs across the BLOCK_SIZE boundary" do upper_bound = U::BLOCK_SIZE * 2 + 10 uuids = (1..upper_bound).map { U.generate } expect(uuids.size).to eq(uuids.uniq.size) end end celluloid-0.18.0/spec/celluloid/mailbox_spec.rb0000644000004100000410000000012114005764144021503 0ustar www-datawww-dataRSpec.describe Celluloid::Mailbox do it_behaves_like "a Celluloid Mailbox" end celluloid-0.18.0/spec/celluloid/probe_spec.rb0000644000004100000410000000556114005764144021174 0ustar www-datawww-datarequire "celluloid/probe" class DummyActor; include Celluloid; end class TestProbeClient include Celluloid include Celluloid::Notifications attr_reader :buffer finalizer :do_unsubscribe def initialize(queue) @events = queue subscribe(/celluloid\.events\..+/, :event_received) end def event_received(topic, args) @events << [topic, args[0], args[1]] end def do_unsubscribe # TODO: shouldn't be necessary return unless Actor[:notifications_fanout] unsubscribe rescue Celluloid::DeadActorError # Something is wrong with the shutdown seq. Whatever... rescue => ex STDERR.puts "Exception while finalizing TestProbeClient: #{ex.inspect}" STDERR.puts "BACKTRACE: #{ex.backtrace * "\n (ex) "}" sleep 5 end end RSpec.describe "Probe", actor_system: :global do let(:logger) { Specs::FakeLogger.current } def addr(actor) return nil unless actor return nil unless actor.mailbox return nil unless actor.mailbox.address actor.mailbox.address rescue Celluloid::DeadActorError "(dead actor)" end def wait_for_match(queue, topic, actor1 = nil, actor2 = nil) started = Time.now.to_f actors = [actor1, actor2] expected = ([topic] + actors.map { |a| addr(a) }).dup received = [] last_event_timestamp = nil Timeout.timeout(5) do loop do event = queue.pop actual = ([event[0]] + event[1..-1].map { |a| addr(a) }).dup received << actual last_event_timestamp = Time.now.to_f return event if actual == expected end end rescue Timeout::Error => e q = Celluloid::Probe::EVENTS_BUFFER unprocessed = [] loop do break if q.empty? name, args = q.pop actual = ([name] + args.map { |a| addr(a) }).dup unprocessed << actual end last_event_offset = if last_event_timestamp last_event_timestamp - started else "(no events ever received)" end raise "wait_for_match: no matching event received for #{topic.inspect}! (#{e.inspect})\n"\ "Expected: #{expected.inspect}\n"\ "Events received: \n #{received.map(&:inspect) * "\n "}\n"\ "Current time offset: #{(Time.now.to_f - started).inspect}\n"\ "Last event offset: #{last_event_offset.inspect}\n"\ "Unprocessed probe events: #{unprocessed.map(&:inspect) * "\n "}\n"\ end def flush_probe_queue # Probe doesn't process the queue periodically, so some events can get in # while previous events are being processed. # # So, we generate another event, so Probe processed the queue (containing # the previously unprocessed event). Celluloid::Actor["an_extra_event"] = Class.new { include Celluloid }.new end let(:queue) { Queue.new } describe ".run" do pending "cannot unsupervise the Probe yet (#573)" end end celluloid-0.18.0/spec/shared/0000755000004100000410000000000014005764144016011 5ustar www-datawww-datacelluloid-0.18.0/spec/shared/actor_examples.rb0000644000004100000410000010366214005764144021354 0ustar www-datawww-dataRSpec.shared_examples "a Celluloid Actor" do around do |ex| Celluloid.boot ex.run Celluloid.shutdown if Celluloid.running? end let(:task_klass) { Celluloid.task_class } let(:actor_class) { ExampleActorClass.create(CelluloidSpecs.included_module, task_klass) } let(:actor) { actor_class.new "Troy McClure" } let(:logger) { Specs::FakeLogger.current } it "doesn't raise an error when rebooting" do Celluloid.shutdown expect { Celluloid.boot }.to change { Celluloid.running? }.from(false).to(true) end it "returns the actor's class, not the proxy's" do expect(actor.class).to eq(actor_class) end it "compares with the actor's class in a case statement" do expect( case actor when actor_class true else false end ).to be_truthy end it "can be stored in hashes" do expect(actor.hash).not_to eq(Kernel.hash) expect(actor.object_id).not_to eq(Kernel.object_id) expect(actor.eql?(actor)).to be_truthy end it "can be stored in hashes even when dead" do actor.terminate expect(actor.dead?).to be_truthy expect(actor.hash).not_to eq(Kernel.hash) expect(actor.object_id).not_to eq(Kernel.object_id) expect(actor.eql?(actor)).to be_truthy end it "implements respond_to? correctly" do expect(actor).to respond_to(:alive?) end it "supports synchronous calls" do expect(actor.greet).to eq("Hi, I'm Troy McClure") end context "with a method accepting a block" do let(:actor) { james_bond_role.new } let(:james_bond_role) do Class.new do include CelluloidSpecs.included_module def act "My name is #{yield('James', 'Bond')}" end def give_role_to(actor, &block) actor.act(&block) end end end it "supports synchronously passing a block to itself through a reference" do result = actor.give_role_to(actor) { |name, surname| "#{surname}. #{name} #{surname}." } expect(result).to eq("My name is Bond. James Bond.") end end it "supports synchronous calls via #method" do method = actor.method(:greet) expect(method.call).to eq("Hi, I'm Troy McClure") end it "supports #arity calls via #method" do method = actor.method(:greet) expect(method.arity).to be(0) method = actor.method(:change_name) expect(method.arity).to be(1) end it "supports #name calls via #method" do method = actor.method(:greet) expect(method.name).to eq(:greet) end it "supports #parameters via #method" do method = actor.method(:greet) expect(method.parameters).to eq([]) method = actor.method(:change_name) expect(method.parameters.first.last).to eq(:new_name) end it "supports future(:method) syntax for synchronous future calls" do future = actor.future :greet expect(future.value).to eq("Hi, I'm Troy McClure") end it "supports future.method syntax for synchronous future calls" do future = actor.future.greet expect(future.value).to eq("Hi, I'm Troy McClure") end context "when a block is passed synchronously to an actor" do let(:actor) { actor_class.new "Blocky Ralboa" } it "the block is called" do block_executed = false actor.run { block_executed = true } expect(block_executed).to be_truthy end end context "when there is a circular synchronous reference" do let(:ponycopter) do Class.new do include CelluloidSpecs.included_module def greet_by_proxy(actor) actor.greet end def to_s "a ponycopter!" end end.new end let(:actor) { actor_class.new ponycopter } it "is called correctly" do expect(ponycopter.greet_by_proxy(actor)).to eq("Hi, I'm a ponycopter!") end end let(:recursive_klass1) do Class.new do include CelluloidSpecs.included_module def recursion_test(recurse_through = nil) if recurse_through recurse_through.recursion_thunk(Celluloid::Actor.current) else Celluloid.detect_recursion end end end end let(:recursive_klass2) do Class.new do include CelluloidSpecs.included_module def recursion_thunk(other) other.recursion_test end end end it "detects recursion" do actor1 = recursive_klass1.new actor2 = recursive_klass2.new expect(actor1.recursion_test).to be_falsey expect(actor1.recursion_test(actor2)).to be_truthy end describe "#respond_to?" do context "with method_missing resolving to :first" do specify { expect(actor).to respond_to(:first) } context "when missing method is called" do specify { expect(actor.first).to be :bar } end end context "with a private method" do specify { expect(actor.respond_to?(:zomg_private)).to be_falsey } context "when :include_private is passed" do specify { expect(actor.respond_to?(:zomg_private, true)).to be_truthy } end end end context "when initialize sleeps" do let(:actor) do Class.new do include CelluloidSpecs.included_module def initialize sleep 0.1 end end.new end it "warns about suspending the initialize" do # rubocop:disable Metrics/LineLength expect(logger).to receive(:warn).with(/Dangerously suspending task: type=:call, meta={:dangerous_suspend=>true, :method_name=>:initialize}, status=:sleeping/) # rubocop:enable Metrics/LineLength actor.terminate Specs.sleep_and_wait_until { !actor.alive? } end end context "with a user defined finalizer" do it "calls the user defined finalizer" do expect(actor.wrapped_object).to receive(:my_finalizer) actor.terminate Specs.sleep_and_wait_until { !actor.alive? } end end context "when actor sleeps in finalizer" do let(:actor) do Class.new do include CelluloidSpecs.included_module finalizer :cleanup def cleanup sleep 0.1 end end.new end it "warns about suspending the finalizer" do allow(logger).to receive(:warn) allow(logger).to receive(:crash).with(/finalizer crashed!/, Celluloid::TaskTerminated) # rubocop:disable Metrics/LineLength expect(logger).to receive(:warn).with(/Dangerously suspending task: type=:finalizer, meta={:dangerous_suspend=>true, :method_name=>:cleanup}, status=:sleeping/) # rubocop:enable Metrics/LineLength actor.terminate Specs.sleep_and_wait_until { !actor.alive? } end end it "supports async(:method) syntax for asynchronous calls" do actor.async :change_name, "Charlie Sheen" expect(actor.greet).to eq("Hi, I'm Charlie Sheen") end it "supports async.method syntax for asynchronous calls" do actor.async.change_name "Charlie Sheen" expect(actor.greet).to eq("Hi, I'm Charlie Sheen") end it "supports async.method syntax for asynchronous calls to itself" do actor.change_name_async "Charlie Sheen" expect(actor.greet).to eq("Hi, I'm Charlie Sheen") end it "allows an actor to call private methods asynchronously" do actor.call_private expect(actor.private_called).to be_truthy end it "knows if it's inside actor scope" do expect(Celluloid).not_to be_actor expect(actor.run do Celluloid.actor? end).to be_falsey expect(actor.run_on_receiver do Celluloid.actor? end).to be_truthy expect(actor).to be_actor end it "inspects properly" do expect(actor.inspect).to match(/Celluloid::Proxy::Cell\(/) expect(actor.inspect).to match(/#{actor_class}/) expect(actor.inspect).to include('@name="Troy McClure"') expect(actor.inspect).not_to include("@celluloid") end it "inspects properly when dead" do actor.terminate Specs.sleep_and_wait_until { !actor.alive? } expect(actor.inspect).to match(/Celluloid::Proxy::Cell\(/) expect(actor.inspect).to match(/#{actor_class}/) expect(actor.inspect).to include("dead") end it "reports private methods properly when dead" do actor.terminate expect { actor.private_methods }.not_to raise_error end context "with actors referencing each other" do let(:klass) do Class.new do include CelluloidSpecs.included_module attr_accessor :other def initialize(other = nil) @other = other end end end it "supports recursive inspect" do itchy = klass.new scratchy = klass.new(itchy) itchy.other = scratchy inspection = itchy.inspect expect(inspection).to match(/Celluloid::Proxy::Cell\(/) expect(inspection).to include("...") end end it "allows access to the wrapped object" do expect(actor.wrapped_object).to be_a actor_class end it "warns about leaked wrapped objects via #inspect" do expect(actor.inspect).not_to include Celluloid::BARE_OBJECT_WARNING_MESSAGE expect(actor.inspect_thunk).not_to include Celluloid::BARE_OBJECT_WARNING_MESSAGE expect(actor.wrapped_object.inspect).to include Celluloid::BARE_OBJECT_WARNING_MESSAGE end it "can override #send" do expect(actor.send("foo")).to eq("oof") end if RUBY_PLATFORM == "java" && Celluloid.task_class != Celluloid::Task::Fibered context "when executing under JRuby" do let(:actor) do Class.new do include CelluloidSpecs.included_module def current_java_thread Thread.current.to_java.getNativeThread end def name_inside_task Thread.current.to_java.getNativeThread.get_name end end.new end it "sets execution info" do expect(actor.name_inside_task).to match(/\[Celluloid\] ##name_inside_task/) end context "when the task if finished" do let(:jthread) { actor.current_java_thread } before do Specs.sleep_and_wait_until { jthread.get_name !~ /^\[Celluloid\] ##current_java_thread$/ } end it "unsets execution info after task completion" do expect(jthread.get_name).to match(/^Ruby-/) end end end end context "mocking methods" do before do expect(actor.wrapped_object).to receive(:external_hello).once.and_return "World" end it "works externally via the proxy" do expect(actor.external_hello).to eq("World") end it "works internally when called on self" do expect(actor.internal_hello).to eq("World") end end context "mocking out the proxy" do it "allows mocking return values" do expect(actor).to receive(:name).and_return "Spiderman" expect(actor.name).to eq "Spiderman" end it "allows mocking raises" do expect(actor).to receive(:greet).and_raise ArgumentError expect { actor.greet }.to raise_error(ArgumentError) expect(actor).to be_alive end xit "allows mocking async calls via the async proxy" do # TODO: Verify via CIA... This appears to be working, with new celluloid/rspec handler. # pending "Fails due to RSpec's new expectation verification" # fail "TODO: may never work in newer versions of RSpec (no method on Proxy::Async)" expect(actor.async).to receive(:greet).once actor.async.greet end it "allows mocking async calls via #async send" do expect(actor).to receive(:async).once.with(:greet) actor.async :greet end end context :exceptions do context "with a dead actor" do let(:actor) { actor_class.new "James Dean" } # is this in bad taste? it "reraises exceptions which occur during synchronous calls in the sender" do allow(logger).to receive(:crash).with("Actor crashed!", ExampleCrash) expect { actor.crash }.to raise_exception(ExampleCrash) Specs.sleep_and_wait_until { !actor.alive? } end it "includes both sender and receiver in exception traces" do allow(logger).to receive(:crash).with("Actor crashed!", ExampleCrash) example_receiver = Class.new do include CelluloidSpecs.included_module define_method(:receiver_method) do raise ExampleCrash, "the spec purposely crashed me :(" end end.new example_caller = Class.new do include CelluloidSpecs.included_module define_method(:sender_method) do example_receiver.receiver_method end end.new expect { example_caller.sender_method }.to raise_error ExampleCrash end it "raises DeadActorError if methods are synchronously called on a dead actor" do allow(logger).to receive(:crash).with("Actor crashed!", ExampleCrash) begin actor.crash rescue ExampleCrash end # TODO: avoid this somehow sleep 0.1 # hax to prevent a race between exit handling and the next call expect { actor.greet }.to raise_exception(Celluloid::DeadActorError) end end end context :abort do let(:actor) { actor_class.new "Al Pacino" } it "raises exceptions in the sender but keeps running" do expect do actor.crash_with_abort "You die motherfucker!", :bar end.to raise_exception(ExampleCrash, "You die motherfucker!") expect(actor).to be_alive end it "converts strings to runtime errors" do expect do actor.crash_with_abort_raw "foo" end.to raise_exception(RuntimeError, "foo") end it "crashes the sender if we pass neither String nor Exception" do number = 10 allow(logger).to receive(:crash).with("Actor crashed!", TypeError) expect do actor.crash_with_abort_raw number end.to raise_exception(TypeError, "Exception object/String expected, but #{number.class} received") Specs.sleep_and_wait_until { !actor.alive? } expect(actor).not_to be_alive end end context :termination do let(:actor) { actor_class.new "Arnold Schwarzenegger" } context "when alive" do specify { expect(actor).to be_alive } specify { expect(actor).to_not be_dead } end context "when terminated" do before do actor.terminate Specs.sleep_and_wait_until { !actor.alive? } end specify { expect(actor).not_to be_alive } context "when terminated!" do specify do expect do actor.terminate! end.to raise_exception(Celluloid::DeadActorError, "actor already terminated") end end end context "when terminated by a Call::Sync" do before do actor.shutdown Specs.sleep_and_wait_until { !actor.alive? } end specify { expect(actor).not_to be_alive } end unless RUBY_PLATFORM == "java" || RUBY_ENGINE == "rbx" context "when killed" do before do Celluloid::Actor.kill(actor) Specs.sleep_and_wait_until { !actor.alive? } end specify { expect(actor).not_to be_alive } specify { expect(actor).to be_dead } context "when called" do specify do expect { actor.greet }.to raise_exception(Celluloid::DeadActorError) end end end context "when celluloid is shutdown" do before do allow(Celluloid::Actor).to receive(:kill).and_call_original actor Celluloid.shutdown end it "terminates cleanly on Celluloid shutdown" do expect(Celluloid::Actor).not_to have_received(:kill) end end end context "when sleeping" do before do actor.async.sleepy 10 actor.greet # make sure the actor has started sleeping end context "when terminated" do it "logs a debug" do # rubocop:disable Metrics/LineLength expect(logger).to receive(:debug).with(/^Terminating task: type=:call, meta={:dangerous_suspend=>false, :method_name=>:sleepy}, status=:sleeping/) # rubocop:enable Metrics/LineLength actor.terminate Specs.sleep_and_wait_until { !actor.alive? } end end end end describe "#current_actor" do context "when called on an actor" do let(:actor) { actor_class.new "Roger Daltrey" } it "knows the current actor" do expect(actor.current_actor).to eq actor end end context "when called outside an actor" do specify { expect { Celluloid.current_actor }.to raise_exception(Celluloid::NotActorError) } end end context :linking do before :each do @kevin = actor_class.new "Kevin Bacon" # Some six degrees action here @charlie = actor_class.new "Charlie Sheen" end let(:supervisor_class) do Class.new do # like a boss include CelluloidSpecs.included_module trap_exit :lambaste_subordinate attr_reader :exception def initialize(name) @name = name @exception = nil @subordinate_lambasted = false end def subordinate_lambasted? @subordinate_lambasted end def lambaste_subordinate(_actor, reason) @subordinate_lambasted = true @exception = reason end end end it "links to other actors" do @kevin.link @charlie expect(@kevin.monitoring?(@charlie)).to be_truthy expect(@kevin.linked_to?(@charlie)).to be_truthy expect(@charlie.monitoring?(@kevin)).to be_truthy expect(@charlie.linked_to?(@kevin)).to be_truthy end it "unlinks from other actors" do @kevin.link @charlie @kevin.unlink @charlie expect(@kevin.monitoring?(@charlie)).to be_falsey expect(@kevin.linked_to?(@charlie)).to be_falsey expect(@charlie.monitoring?(@kevin)).to be_falsey expect(@charlie.linked_to?(@kevin)).to be_falsey end it "monitors other actors unidirectionally" do @kevin.monitor @charlie expect(@kevin.monitoring?(@charlie)).to be_truthy expect(@kevin.linked_to?(@charlie)).to be_falsey expect(@charlie.monitoring?(@kevin)).to be_falsey expect(@charlie.linked_to?(@kevin)).to be_falsey end it "unmonitors other actors" do @kevin.monitor @charlie @kevin.unmonitor @charlie expect(@kevin.monitoring?(@charlie)).to be_falsey expect(@kevin.linked_to?(@charlie)).to be_falsey expect(@charlie.monitoring?(@kevin)).to be_falsey expect(@charlie.linked_to?(@kevin)).to be_falsey end it "traps exit messages from other actors" do allow(logger).to receive(:crash).with("Actor crashed!", ExampleCrash) chuck = supervisor_class.new "Chuck Lorre" chuck.link @charlie expect do @charlie.crash end.to raise_exception(ExampleCrash) sleep 0.1 # hax to prevent a race between exit handling and the next call expect(chuck).to be_subordinate_lambasted end it "traps exit messages from other actors in subclasses" do allow(logger).to receive(:crash).with("Actor crashed!", ExampleCrash) supervisor_subclass = Class.new(supervisor_class) chuck = supervisor_subclass.new "Chuck Lorre" chuck.link @charlie expect do @charlie.crash end.to raise_exception(ExampleCrash) sleep 0.1 # hax to prevent a race between exit handling and the next call expect(chuck).to be_subordinate_lambasted end it "unlinks from a dead linked actor" do allow(logger).to receive(:crash).with("Actor crashed!", ExampleCrash) chuck = supervisor_class.new "Chuck Lorre" chuck.link @charlie expect do @charlie.crash end.to raise_exception(ExampleCrash) sleep 0.1 # hax to prevent a race between exit handling and the next call expect(chuck.links.count).to be(0) end it "traps exit reason from subordinates" do allow(logger).to receive(:crash).with("Actor crashed!", ExampleCrash) chuck = supervisor_class.new "Chuck Lorre" chuck.link @charlie expect do @charlie.crash end.to raise_exception(ExampleCrash) sleep 0.1 # hax to prevent a race between exit handling and the next call expect(chuck.exception.class).to be(ExampleCrash) end end context :signaling do before do @signaler = Class.new do include CelluloidSpecs.included_module def initialize @waiting = false @signaled = false end def wait_for_signal raise "already signaled" if @signaled @waiting = true value = wait :ponycopter @waiting = false @signaled = true value end def send_signal(value) signal :ponycopter, value end def waiting? @waiting end def signaled? @signaled end end end it "allows methods within the same object to signal each other" do obj = @signaler.new expect(obj).not_to be_signaled obj.async.wait_for_signal expect(obj).not_to be_signaled expect(obj).to be_waiting obj.send_signal :foobar expect(obj).to be_signaled expect(obj).not_to be_waiting end it "sends values along with signals" do obj = @signaler.new expect(obj).not_to be_signaled future = obj.future(:wait_for_signal) expect(obj).to be_waiting expect(obj).not_to be_signaled expect(obj.send_signal(:foobar)).to be_truthy expect(future.value).to be(:foobar) end end context :exclusive do subject do Class.new do include CelluloidSpecs.included_module attr_reader :tasks def initialize @tasks = [] end def log_task(task) @tasks << task end def exclusive_with_block_log_task(task) exclusive do sleep Specs::TIMER_QUANTUM log_task(task) end end def exclusive_log_task(task) sleep Specs::TIMER_QUANTUM log_task(task) end exclusive :exclusive_log_task def check_not_exclusive Celluloid.exclusive? end def check_exclusive exclusive { Celluloid.exclusive? } end def nested_exclusive_example exclusive { exclusive { nil }; Celluloid.exclusive? } end end.new end it "executes methods in the proper order with block form" do subject.async.exclusive_with_block_log_task(:one) subject.async.log_task(:two) sleep Specs::TIMER_QUANTUM * 2 expect(subject.tasks).to eq(%i[one two]) end it "executes methods in the proper order with a class-level annotation" do subject.async.exclusive_log_task :one subject.async.log_task :two sleep Specs::TIMER_QUANTUM * 2 expect(subject.tasks).to eq(%i[one two]) end it "knows when it's in exclusive mode" do expect(subject.check_not_exclusive).to be_falsey expect(subject.check_exclusive).to be_truthy end it "remains in exclusive mode inside nested blocks" do expect(subject.nested_exclusive_example).to be_truthy end end context "exclusive classes" do subject do Class.new do include CelluloidSpecs.included_module exclusive attr_reader :tasks def initialize @tasks = [] end def eat_donuts sleep Specs::TIMER_QUANTUM @tasks << "donuts" end def drink_coffee @tasks << "coffee" end end end context "with two async methods called" do let(:actor) { subject.new } before do actor.async.eat_donuts actor.async.drink_coffee sleep Specs::TIMER_QUANTUM * 2 end it "executes in an exclusive order" do expect(actor.tasks).to eq(%w[donuts coffee]) end end end context :receiving do before do @receiver = Class.new do include CelluloidSpecs.included_module execute_block_on_receiver :signal_myself def signal_myself(obj, &block) current_actor.mailbox << obj receive(&block) end end end let(:receiver) { @receiver.new } let(:message) { Object.new } it "allows unconditional receive" do expect(receiver.signal_myself(message)).to eq(message) end it "allows arbitrary selective receive" do received_obj = receiver.signal_myself(message) { |o| o == message } expect(received_obj).to eq(message) end context "when exceeding a given time out" do let(:interval) { 0.1 } it "times out" do # Barely didn't make it once on MRI, so attempting to "unrefactor" started_at = Time.now result = receiver.receive(interval) { false } ended_at = Time.now - started_at expect(result).to_not be expect(ended_at).to be_within(Specs::TIMER_QUANTUM).of interval end end end context :timers do let(:actor) do Class.new do include CelluloidSpecs.included_module def initialize @sleeping = false @fired = false end def do_sleep(n) @sleeping = true sleep n @sleeping = false end def sleeping? @sleeping end def fire_after(n) after(n) { @fired = true } end def fire_every(n) @fired = 0 every(n) { @fired += 1 } end def fired? !!@fired end attr_reader :fired end.new end let(:interval) { Specs::TIMER_QUANTUM * 10 } let(:sleep_interval) { interval + Specs::TIMER_QUANTUM } # wonky! #/ it "suspends execution of a method (but not the actor) for a given time" do # Sleep long enough to ensure we're actually seeing behavior when asleep # but not so long as to delay the test suite unnecessarily started_at = Time.now future = actor.future(:do_sleep, interval) sleep(interval / 2) # wonky! :/ expect(actor).to be_sleeping future.value # I got 0.558 (in a slightly busy MRI) which is outside 0.05 of 0.5, so let's use (0.05 * 2) expect(Time.now - started_at).to be_within(Specs::TIMER_QUANTUM * 2).of interval end it "schedules timers which fire in the future" do actor.fire_after(interval) expect(actor).not_to be_fired sleep sleep_interval expect(actor).to be_fired end it "schedules recurring timers which fire in the future" do actor.fire_every(interval) expect(actor.fired).to be_zero sleep sleep_interval expect(actor.fired).to be 1 2.times { sleep sleep_interval } expect(actor.fired).to be 3 end it "cancels timers before they fire" do timer = actor.fire_after(interval) expect(actor).not_to be_fired timer.cancel sleep sleep_interval expect(actor).not_to be_fired end it "allows delays from outside the actor" do fired = false actor.after(interval) { fired = true } expect(fired).to be_falsey sleep sleep_interval expect(fired).to be_truthy end end context :tasks do let(:actor) do Class.new do include CelluloidSpecs.included_module attr_reader :blocker def initialize @blocker = Class.new do include Celluloid def block wait :unblock end def unblock signal :unblock end end.new end def blocking_call @blocker.block end end.new end it "knows which tasks are waiting on calls to other actors" do tasks = actor.tasks expect(tasks.size).to be 1 actor.future(:blocking_call) sleep 0.1 # hax! waiting for ^^^ call to actually start tasks = actor.tasks expect(tasks.size).to be 2 blocking_task = tasks.find { |t| t.status != :running } expect(blocking_task).to be_a task_klass expect(blocking_task.status).to be :callwait actor.blocker.unblock sleep 0.1 # hax again :( expect(actor.tasks.size).to be 1 end end context :mailbox_class do class ExampleMailbox < Celluloid::Mailbox; end subject do Class.new do include CelluloidSpecs.included_module mailbox_class ExampleMailbox end end it "uses user-specified mailboxes" do expect(subject.new.mailbox).to be_a ExampleMailbox end it "retains custom mailboxes when subclassed" do subclass = Class.new(subject) expect(subclass.new.mailbox).to be_a ExampleMailbox end end context :mailbox_size do subject do Class.new do include CelluloidSpecs.included_module mailbox_size 100 end end it "configures the mailbox limit" do expect(subject.new.mailbox.max_size).to eq(100) end end context "#proxy_class" do subject do Class.new do include CelluloidSpecs.included_module klass = Class.new(Celluloid::Proxy::Cell) do def subclass_proxy? true end end proxy_class klass end end it "uses user-specified proxy" do expect { subject.new.subclass_proxy? }.to_not raise_error end it "retains custom proxy when subclassed" do subclass = Class.new(subject) expect(subclass.new.subclass_proxy?).to be(true) end context "when overriding a actor's method" do subject do Class.new do include CelluloidSpecs.included_module klass = Class.new(Celluloid::Proxy::Cell) do def dividing_3_by(number) raise ArgumentError, "" if number.zero? super end end def dividing_3_by(number) 3 / number end proxy_class klass end.new end context "when invoked with an invalid parameter for that method" do it "calls the overloaded method" do expect { subject.dividing_3_by(0) }.to raise_error(ArgumentError, "") end it "does not crash the actor" do begin subject.dividing_3_by(0) rescue ArgumentError end expect(subject.dividing_3_by(3)).to eq(1) end end end context "when it includes method checking" do module MethodChecking def method_missing(meth, *args) return super if %i[__send__ respond_to? method class __class__].include? meth unmet_requirement = nil arity = method(meth).arity if arity >= 0 unmet_requirement = arity.to_s if args.size != arity elsif arity < -1 mandatory_args = -arity - 1 unmet_requirement = "#{mandatory_args}+" if args.size < mandatory_args end raise ArgumentError, "wrong number of arguments (#{args.size} for #{unmet_requirement})" if unmet_requirement super end end subject do Class.new do include CelluloidSpecs.included_module klass = Class.new(Celluloid::Proxy::Cell) do include MethodChecking end def madness "This is madness!" end def this_is_not_madness(word1, word2, word3, *_args) raise "This is madness!" unless [word1, word2, word3] == %i[this is Sparta] end proxy_class klass end.new end context "when invoking a non-existing method" do it "raises a NoMethodError" do expect { subject.method_to_madness }.to raise_error(NoMethodError) end it "does not crash the actor" do begin subject.method_to_madness rescue NoMethodError end expect(subject.madness).to eq("This is madness!") end end context "when invoking a existing method with incorrect args" do context "with too many arguments" do it "raises an ArgumentError" do expect { subject.madness(:Sparta) } .to raise_error(ArgumentError, "wrong number of arguments (1 for 0)") end it "does not crash the actor" do begin subject.madness(:Sparta) rescue ArgumentError end expect(subject.madness).to eq("This is madness!") end end context "with not enough mandatory arguments" do it "raises an ArgumentError" do expect { subject.this_is_not_madness(:this, :is) } .to raise_error(ArgumentError, "wrong number of arguments (2 for 3+)") end end end end end context :task_class do class ExampleTask < Celluloid.task_class; end subject do Class.new do include CelluloidSpecs.included_module task_class ExampleTask end end it "overrides the task class" do expect(subject.new.tasks.first).to be_a ExampleTask end it "retains custom task classes when subclassed" do subclass = Class.new(subject) expect(subclass.new.tasks.first).to be_a ExampleTask end end context :timeouts do let :actor_class do Class.new do include CelluloidSpecs.included_module def name sleep 0.5 :foo end def ask_name_with_timeout(other, duration) timeout(duration) { other.name } end end end let(:a1) { actor_class.new } let(:a2) { actor_class.new } it "allows timing out tasks, raising Celluloid::TaskTimeout" do allow(logger).to receive(:crash).with("Actor crashed!", Celluloid::TaskTimeout) expect { a1.ask_name_with_timeout a2, 0.3 }.to raise_error(Celluloid::TaskTimeout) Specs.sleep_and_wait_until { !a1.alive? } end it "does not raise when it completes in time" do expect(a1.ask_name_with_timeout(a2, 0.6)).to eq(:foo) end end context "raw message sends" do it "logs on unhandled messages" do expect(logger).to receive(:debug).with("Discarded message (unhandled): first") actor.mailbox << :first sleep Specs::TIMER_QUANTUM end end end celluloid-0.18.0/spec/shared/group_examples.rb0000644000004100000410000000553314005764144021376 0ustar www-datawww-dataRSpec.shared_examples "a Celluloid Group" do let!(:queue) { Queue.new } let!(:busy_queue) { Queue.new } let(:logger) { Specs::FakeLogger.current } def wait_until_busy(busy_queue = nil) return busy_queue.pop if busy_queue Specs.sleep_and_wait_until { subject.busy? } end def wait_until_idle Specs.sleep_and_wait_until { subject.idle? } end before { subject } after do subject.shutdown end it "gets threads" do expect(subject.get { queue.pop }).to be_a Thread queue << nil wait_until_idle end [::StandardError, ::Exception].each do |exception_class| context "with an #{exception_class} in the thread" do before do @wait_queue = Queue.new # doesn't work if in a let() allow(logger).to receive(:crash) subject.get do busy_queue << nil @wait_queue.pop raise exception_class, "Error" end wait_until_busy(busy_queue) end it "logs the crash" do expect(logger).to receive(:crash).with("thread crashed", exception_class) @wait_queue << nil # let the thread fail wait_until_idle end it "puts error'd threads back" do @wait_queue << nil # let the thread fail wait_until_idle expect(subject.idle?).to be_truthy end end end context "when a thread has local variables" do before do @thread = subject.get do Thread.current[:foo] = :bar queue.pop end wait_until_busy queue << nil # let the thread finish if Celluloid.group_class == Celluloid::Group::Pool # Wait until we get the same thread for a different proc Specs.sleep_and_wait_until { subject.get { sleep 0.1 } == @thread } else wait_until_idle end end # Cleaning not necessary for Spawner unless Celluloid.group_class == Celluloid::Group::Spawner it "cleans thread locals from old threads" do expect(@thread[:foo]).to be_nil end end end it "shuts down" do subject thread = Queue.new expect( subject.get do thread << Thread.current sleep end ).to be_a(Celluloid::Thread) thread.pop # wait for 3rd-party thread to get strated expect(subject.active?).to eq true subject.shutdown expect(subject.active?).to eq false expect(subject.group.length).to eq 0 end context "with a dead thread" do before do if Celluloid.group_class == Celluloid::Group::Pool subject.max_idle = 0 # Instruct the pool to immediately shut down the thread. end subject.get { queue.pop } wait_until_busy queue << nil wait_until_idle subject.shutdown if Celluloid.group_class == Celluloid::Group::Spawner end it "doesn't leak dead threads" do expect(subject.to_a.size).to eq(0) end end end celluloid-0.18.0/spec/shared/task_examples.rb0000644000004100000410000000216214005764144021177 0ustar www-datawww-dataclass MockActor attr_reader :tasks def initialize @tasks = [] end def setup_thread; end end RSpec.shared_examples "a Celluloid Task" do let(:task_type) { :foobar } let(:suspend_state) { :doing_something } let(:actor) { MockActor.new } subject { Celluloid.task_class.new(task_type, {}) { Celluloid::Task.suspend(suspend_state) } } before :each do Thread.current[:celluloid_actor_system] = Celluloid.actor_system Thread.current[:celluloid_actor] = actor end after :each do Thread.current[:celluloid_actor_system].shutdown Thread.current[:celluloid_actor] = nil Thread.current[:celluloid_actor_system] = nil end it "begins with status :new" do expect(subject.status).to be :new end it "resumes" do expect(subject).to be_running subject.resume expect(subject.status).to eq(suspend_state) subject.resume expect(subject).not_to be_running end it "raises exceptions outside" do task = Celluloid.task_class.new(task_type, {}) do raise "failure" end expect do task.resume end.to raise_exception("failure") end end celluloid-0.18.0/spec/shared/mailbox_examples.rb0000644000004100000410000000432114005764144021667 0ustar www-datawww-dataRSpec.shared_examples "a Celluloid Mailbox" do after do allow(Celluloid.logger).to receive(:debug) subject.shutdown if subject.alive? end it "receives messages" do message = :ohai subject << message expect(subject.receive).to eq(message) end it "prioritizes system events over other messages" do subject << :dummy1 subject << :dummy2 subject << Celluloid::SystemEvent.new expect(subject.receive).to be_a(Celluloid::SystemEvent) end it "selectively receives messages with a block" do class Foo; end class Bar; end class Baz; end foo = Foo.new bar = Bar.new baz = Baz.new subject << baz subject << foo subject << bar expect(subject.receive { |msg| msg.is_a? Foo }).to eq(foo) expect(subject.receive { |msg| msg.is_a? Bar }).to eq(bar) expect(subject.receive).to eq(baz) end it "waits for a given timeout interval" do interval = 0.1 started_at = Time.now expect do subject.receive(interval) { false } end.to raise_exception(Celluloid::TaskTimeout) # Just check to make sure it didn't return earlier expect(Time.now - started_at).to be >= interval end it "has a size" do expect(subject).to respond_to(:size) expect(subject.size).to be_zero subject << :foo subject << :foo expect(subject.entries.size).to eq(2) end it "discards messages received when when full" do subject.max_size = 2 subject << :first subject << :second subject << :third expect(subject.to_a).to match_array(%i[first second]) end it "logs discarded messages" do expect(Celluloid.logger).to receive(:debug).with("Discarded message (mailbox is dead): third") subject.max_size = 2 subject << :first subject << :second subject << :third end it "discard messages when dead" do expect(Celluloid.logger).to receive(:debug).with("Discarded message (mailbox is dead): first") expect(Celluloid.logger).to receive(:debug).with("Discarded message (mailbox is dead): second") expect(Celluloid.logger).to receive(:debug).with("Discarded message (mailbox is dead): third") subject << :first subject << :second subject.shutdown subject << :third end end celluloid-0.18.0/spec/shared/stack_examples.rb0000644000004100000410000000522314005764144021343 0ustar www-datawww-dataRSpec.shared_examples "a Celluloid Stack" do let(:actor_system) { Celluloid::Actor::System.new } let(:threads) { Queue.new } before(:each) do items = 0 [Celluloid::Task::Fibered, Celluloid::Task::Threaded].each do |task_klass| create_async_blocking_actor(task_klass) items += 1 end @active_thread = create_thread_with_role(threads, :other_thing) items += 1 @idle_thread = create_thread_with_role(threads, :idle_thing) items += 1 # StackWaiter for each thread to add itself to the queue tmp = Queue.new items.times do th = Timeout.timeout(4) { threads.pop } tmp << th end expect(threads).to be_empty # put threads back into the queue for killing threads << tmp.pop until tmp.empty? end after do StackWaiter.no_longer actor_system.shutdown end describe "#actors" do it "should include all actors" do expect(subject.actors.size).to eq(2) end end describe "#threads" do # TODO: this spec should use mocks because it's non-deterministict it "should include threads that are not actors" do # TODO: Assess need for bypassing StackWaiter.forever's QUEUE.pop after #670 & #678 # NOTE: Pool#each doesn't guarantee to contain the newly started thread # because the actor's methods (which create and store the thread handle) # are started asynchronously. # # The mutexes in InternalPool especially can cause additional delay - # causing Pool#get to wait for IPool#each to free the mutex before the # new thread can be stored. # # And, Internals::StackDump#threads is cached, so we have to reconstruct the # Internals::StackDump until it matches reality. # # Also, the actual number of threads and how InternalPool juggles them is # non deterministic to begin with: # # 2 actors # -> *0-1 task threads # # *1 idle thread # *1 active thread # # Together: 3-4 threads # Pool somehow doesn't create extra tasks # 5 is on JRuby-head expected = Celluloid.group_class == Celluloid::Group::Pool ? [3, 4] : [3, 4, 5, 6] expect(expected).to include(subject.threads.size) end it "should include idle threads" do expect(subject.threads.map(&:thread_id)).to include(@idle_thread.object_id) end it "should include threads checked out of the group for roles other than :actor" do expect(subject.threads.map(&:thread_id)).to include(@active_thread.object_id) end it "should have the correct roles" do expect(subject.threads.map(&:role)).to include(:idle_thing, :other_thing) end end end celluloid-0.18.0/spec/spec_helper.rb0000644000004100000410000000020214005764144017353 0ustar www-datawww-datarequire "rspec/retry" require "celluloid/rspec" require "celluloid/supervision" Dir[*Specs::INCLUDE_PATHS].map { |f| require f } celluloid-0.18.0/spec/support/0000755000004100000410000000000014005764144016257 5ustar www-datawww-datacelluloid-0.18.0/spec/support/logging.rb0000644000004100000410000000074114005764144020234 0ustar www-datawww-datamodule Specs class << self def logger @logger ||= default_logger.tap { |log| log.level = Logger::WARN } end attr_writer :logger private def default_logger Logger.new(STDERR) end def open_logfile(rel_path, sync) root = Pathname(__FILE__).dirname.dirname.dirname log_path = root + rel_path log_path.dirname.mkpath logfile = File.open(log_path.to_s, "a") logfile.sync if sync logfile end end end celluloid-0.18.0/spec/support/includer.rb0000644000004100000410000000026614005764144020415 0ustar www-datawww-datamodule CelluloidSpecs def self.included_module # Celluloid::IO implements this with with 'Celluloid::IO' (defined? INCLUDED_MODULE) ? INCLUDED_MODULE : Celluloid end end celluloid-0.18.0/spec/support/examples/0000755000004100000410000000000014005764144020075 5ustar www-datawww-datacelluloid-0.18.0/spec/support/examples/stack_methods.rb0000644000004100000410000000100214005764144023243 0ustar www-datawww-datadef create_async_blocking_actor(task_klass) actor_klass = Class.new(StackBlocker) do task_class task_klass end actor = actor_system.within do actor_klass.new(threads) end actor.async.blocking end def create_thread_with_role(threads, role) resume = Queue.new thread = actor_system.get_thread do resume.pop # to avoid race for 'thread' variable thread.role = role threads << thread StackWaiter.forever end resume << nil # to avoid race for 'thread' variable thread end celluloid-0.18.0/spec/support/examples/actor_class.rb0000644000004100000410000000454114005764144022723 0ustar www-datawww-dataclass ExampleCrash < Celluloid::Error attr_accessor :foo end module ExampleActorClass def self.create(included_module, task_klass) Class.new do include included_module task_class task_klass attr_reader :name finalizer :my_finalizer execute_block_on_receiver :run_on_receiver def initialize(name) @name = name @delegate = [:bar] end def sleepy(duration) sleep duration end def change_name(new_name) @name = new_name end def change_name_async(new_name) async.change_name new_name end def greet "Hi, I'm #{@name}" end def actor? Celluloid.actor? end def run(*args) yield(*args) end def run_on_receiver(*args) yield(*args) end def crash raise ExampleCrash, "the spec purposely crashed me :(" end def crash_with_abort(reason, foo = nil) example_crash = ExampleCrash.new(reason) example_crash.foo = foo abort example_crash end def crash_with_abort_raw(reason) abort reason end def internal_hello external_hello end def external_hello "Hello" end def inspect_thunk inspect end def send(string) string.reverse end def shutdown terminate end # Ideally, this class should implement "fake methods" # consistently, including :methods, :public_methods, etc. def method_missing(method_name, *args, &block) if delegates?(method_name) @delegate.send method_name, *args, &block else super end end def respond_to?(method_name, include_private = false) delegates?(method_name) || super end def method(method_name) if delegates?(method_name) @delegate.method(method_name) else super end end def call_private async.zomg_private end def zomg_private @private_called = true end private :zomg_private attr_reader :private_called def my_finalizer; end private def delegates?(method_name) return false unless @delegate ||= nil @delegate.respond_to?(method_name) end end end end celluloid-0.18.0/spec/support/examples/call_class.rb0000644000004100000410000000106514005764144022524 0ustar www-datawww-dataclass CallExampleActor include Celluloid def initialize(next_actor = nil) @next = next_actor end def actual_method; end def inspect raise "Don't call!" end def chained_call_ids [call_chain_id, @next.call_chain_id] end end # de DEPRECATE: class DeprecatedCallExampleActor include Celluloid def initialize(next_actor = nil) @next = next_actor end def actual_method; end def inspect raise "Please don't call me! I'm not ready yet!" end def chained_call_ids [call_chain_id, @next.call_chain_id] end end celluloid-0.18.0/spec/support/examples/stack_classes.rb0000644000004100000410000000226714005764144023253 0ustar www-datawww-dataclass StackWaiter QUEUE = Queue.new WAITERS = Queue.new ACTORS = Queue.new class << self def forever WAITERS << Thread.current # de QUEUE.pop sleep end def no_longer StackWaiter::ACTORS.pop.terminate until StackWaiter::ACTORS.empty? loop do break if WAITERS.empty? QUEUE << nil nicely_end_thread(WAITERS.pop) end end def nicely_end_thread(th) return if jruby_fiber?(th) status = th.status case status when nil, false, "dead" when "aborting" th.join(2) || STDERR.puts("Thread join timed out...") when "sleep", "run" th.kill th.join(2) || STDERR.puts("Thread join timed out...") else STDERR.puts "unknown status: #{th.status.inspect}" end end def jruby_fiber?(th) return false unless RUBY_PLATFORM == "java" && (java_th = th.to_java.getNativeThread) /Fiber/ =~ java_th.get_name end end end class StackBlocker include Celluloid def initialize(threads) @threads = threads end def blocking StackWaiter::ACTORS << Thread.current @threads << Thread.current StackWaiter.forever end end celluloid-0.18.0/spec/support/examples/evented_mailbox_class.rb0000644000004100000410000000067214005764144024761 0ustar www-datawww-dataclass TestEventedMailbox < Celluloid::Mailbox::Evented class Reactor def initialize @condition = ConditionVariable.new @mutex = Mutex.new end def wakeup @mutex.synchronize do @condition.signal end end def run_once(timeout) @mutex.synchronize do @condition.wait(@mutex, timeout) end end def shutdown; end end def initialize super(Reactor) end end celluloid-0.18.0/spec/support/examples/container_class.rb0000644000004100000410000000064414005764144023575 0ustar www-datawww-dataclass SupervisionContainerHelper @queue = nil class << self def reset! @queue = Queue.new end def done! @queue << :done end def pop! @queue.pop end end end class MyContainerActor include Celluloid attr_reader :args def initialize(*args) @args = args ready end def running? :yep end def ready SupervisionContainerHelper.done! end end celluloid-0.18.0/spec/support/examples/subordinate_class.rb0000644000004100000410000000050414005764144024125 0ustar www-datawww-dataclass SubordinateDead < Celluloid::Error; end class Subordinate include Celluloid attr_reader :state def initialize(state) @state = state end def crack_the_whip case @state when :idle @state = :working else raise SubordinateDead, "the spec purposely crashed me :(" end end end celluloid-0.18.0/spec/support/coverage.rb0000644000004100000410000000004414005764144020375 0ustar www-datawww-datarequire "coveralls" Coveralls.wear! celluloid-0.18.0/spec/support/crash_checking.rb0000644000004100000410000000251214005764144021537 0ustar www-datawww-datamodule Specs class FakeLogger class << self def current allowed_logger.first end attr_accessor :allowed_logger end def initialize(real_logger, example) @mutex = Mutex.new @real_logger = real_logger @crashes = Queue.new @details = nil @example = example self.class.allowed_logger = [self, example] end def crash(*args) check raise "Testing block has already ended!" if @details @crashes << [args, caller.dup] end def debug(*args) check @real_logger.debug(*args) end def warn(*args) check @real_logger.warn(*args) end def with_backtrace(_backtrace) check yield self end def crashes check @mutex.synchronize do return @details if @details @details = [] @details << @crashes.pop until @crashes.empty? @crashes = nil @details end end def crashes? check !crashes.empty? end private def check return if self.class.allowed_logger.first == self raise "Incorrect logger used:"\ " active/allowed: \n#{clas.allowed_logger.inspect},\n"\ " actual/self: \n#{[self, @example].inspect}\n"\ " (maybe an actor from another test is still running?)" end end end celluloid-0.18.0/spec/support/sleep_and_wait.rb0000644000004100000410000000056014005764144021563 0ustar www-datawww-datamodule Specs def self.sleep_and_wait_until(timeout = 10) t1 = Time.now.to_f ::Timeout.timeout(timeout) do loop until yield end diff = Time.now.to_f - t1 STDERR.puts "wait took a bit long: #{diff} seconds" if diff > Specs::TIMER_QUANTUM rescue Timeout::Error t2 = Time.now.to_f raise "Timeout after: #{t2 - t1} seconds" end end celluloid-0.18.0/spec/support/debugging.rb0000644000004100000410000000145114005764144020540 0ustar www-datawww-datamodule Debugging # Attempt to safely convert an object to a useful string (without side # effects if possible) # # Feel free to add support for more objects/responses/etc. def self.dump(obj) case obj when ::Symbol obj.inspect when ::String obj when ::Regexp obj.inspect when ::IO obj.inspect when ::Array obj.map { |a| Debugging.dump(a) }.to_s when ::Celluloid::Response::Success "SuccessResponse(#{dump(obj.value)}) (#{dump(obj.call)})" when ::Celluloid::Call args = obj.arguments.map { |a| Debugging.dump(a) } "Call: #{obj}-> #{obj.method.inspect}(#{args.join(', ')})" else begin obj.__send__(:__class__).to_s rescue NoMethodError obj.__send__(:class).to_s end end end end celluloid-0.18.0/spec/support/loose_threads.rb0000644000004100000410000000336314005764144021444 0ustar www-datawww-datamodule Specs class << self def loose_threads Thread.list.map do |thread| next unless thread next if thread == Thread.current if RUBY_PLATFORM == "java" # Avoid disrupting jRuby's "fiber" threads. name = thread.to_java.getNativeThread.get_name next if /Fiber/ =~ name next unless /^Ruby-/ =~ name backtrace = thread.backtrace # avoid race maybe next unless backtrace next if backtrace.empty? # possibly a timer thread else # Sometimes stays next if thread.backtrace.nil? next unless thread.backtrace.is_a?(Array) next if thread.backtrace.empty? next if thread.backtrace.first =~ /timeout\.rb/ end thread end.compact end def thread_name(thread) RUBY_PLATFORM == "java" ? thread.to_java.getNativeThread.get_name : "" end def assert_no_loose_threads!(location) loose = Specs.loose_threads backtraces = loose.map do |thread| name = thread_name(thread) description = "#{thread.inspect}#{name.empty? ? '' : "(#{name})"}" "Runaway thread: ================ #{description}\n" \ "Backtrace: \n ** #{thread.backtrace * "\n ** "}\n" end return if loose.empty? if RUBY_PLATFORM == "java" && !Nenv.ci? STDERR.puts "Aborted due to runaway threads (#{location})\n"\ "List: (#{loose.map(&:inspect)})\n:#{backtraces.join("\n")}" STDERR.puts "Sleeping so you can investigate on the Java side...." sleep end raise Celluloid::ThreadLeak, "Aborted due to runaway threads (#{location})\n"\ "List: (#{loose.map(&:inspect)})\n:#{backtraces.join("\n")}" end end end celluloid-0.18.0/spec/support/configure_rspec.rb0000644000004100000410000000476114005764144021771 0ustar www-datawww-dataRSpec.configure do |config| config.run_all_when_everything_filtered = true config.disable_monkey_patching! config.profile_examples = 3 config.filter_gems_from_backtrace(*Specs::BACKTRACE_OMITTED) config.verbose_retry = true config.default_retry_count = Specs::ALLOW_RETRIES config.display_try_failure_messages = true config.default_sleep_interval = 1 config.exceptions_to_retry = [Timeout::Error, Celluloid::ThreadLeak] config.mock_with :rspec do |mocks| mocks.verify_doubled_constant_names = true mocks.verify_partial_doubles = true end config.before(:suite) do Specs.stub_out_class_method(Celluloid::Internals::Logger, :crash) do |*args| _name, ex = *args raise "Unstubbed Logger.crash() was called:\n crash(\n #{args.map(&:inspect).join(",\n ")})"\ "\nException backtrace: \n (#{ex.class}) #{ex.backtrace * "\n (#{ex.class}) "}" end end config.before(:each) do |example| @fake_logger = Specs::FakeLogger.new(Celluloid.logger, example.description) stub_const("Celluloid::Internals::Logger", @fake_logger) end config.around do |ex| # Needed because some specs mock/stub/expect on the logger Celluloid.logger = Specs.logger Celluloid.actor_system = nil Specs.reset_class_variables(ex.description) do Timeout.timeout(Specs::MAX_EXECUTION) { ex.run } end if @fake_logger.crashes? crashes = @fake_logger.crashes.map do |args, call_stack| msg, ex = *args "\n** Crash: #{msg.inspect}(#{ex.inspect})\n Backtrace:\n (crash) #{call_stack * "\n (crash) "}"\ "\n Exception Backtrace (#{ex.inspect}):\n (ex) #{ex.backtrace * "\n (ex) "}" end.join("\n") raise "Actor crashes occurred (please stub/mock if these are expected): #{crashes}" end @fake_logger = nil Specs.assert_no_loose_threads!("after example: #{ex.description}") if Specs::CHECK_LOOSE_THREADS end config.around :each, library: :IO do |ex| Celluloid.init FileUtils.rm("/tmp/cell_sock") if File.exist?("/tmp/cell_sock") ex.run Celluloid.shutdown end config.around :each, library: :ZMQ do |ex| Celluloid::ZMQ.init(1) unless ex.metadata[:no_init] Celluloid.boot ex.run Celluloid.shutdown Celluloid::ZMQ.terminate end config.around :each, actor_system: :global do |ex| Celluloid.boot ex.run Celluloid.shutdown end config.around :each, actor_system: :within do |ex| Celluloid::Actor::System.new.within do ex.run end end end celluloid-0.18.0/spec/support/reset_class_variables.rb0000644000004100000410000000152614005764144023147 0ustar www-datawww-datamodule Specs class << self def reset_class_variables(description) # build uuid from example ending (most unique) uuid_prefix = description[-[description.size, 20].min..-1] reset_uuid(uuid_prefix) reset_probe(Queue.new) yield reset_probe(nil) end def reset_probe(value) # !!! DO NOT INTRODUCE ADDITIONAL GLOBAL VARIABLES !!! # rubocop:disable Style/GlobalVars $CELLULOID_MONITORING = !value.nil? # rubocop:enable Style/GlobalVars replace_const(Celluloid::Probe, :EVENTS_BUFFER, value) end def reset_uuid(uuid_prefix) replace_const(Celluloid::Internals::UUID, :PREFIX, uuid_prefix) end def replace_const(klass, const, value) klass.send(:remove_const, const) if klass.const_defined?(const) klass.const_set(const, value) end end end celluloid-0.18.0/spec/support/stubbing.rb0000644000004100000410000000064514005764144020426 0ustar www-datawww-datamodule Specs def self.stub_out_class_method(mod, meth) meta = (class << mod; self; end) original_meth = "original_#{meth}".to_sym raise "ALREADY TRACED: #{mod}.#{meth}" if mod.respond_to?(original_meth) meta.send(:alias_method, original_meth, meth) meta.send(:define_method, meth) do |*args, &block| yield(*args) if block_given? mod.send original_meth, *args, &block end end end celluloid-0.18.0/examples/0000755000004100000410000000000014005764144015427 5ustar www-datawww-datacelluloid-0.18.0/examples/ring.rb0000755000004100000410000000204614005764144016720 0ustar www-datawww-data#!/usr/bin/env ruby $LOAD_PATH.push File.expand_path("../lib", __dir__) require "celluloid/autostart" class Ring include Celluloid class Node include Celluloid def initialize(link) @link = link end def around(n) @link.async.around n end end def initialize(size) @node = Node.new_link current_actor size.times do @node = Node.new_link @node end end # Go around the ring the given number of times def run(n) raise ArgumentError, "I can't go around a negative number of times" if n < 0 async.around n wait :done end # Go around the ring the given number of times def around(n) if n.zero? signal :done else @node.async.around n - 1 end end end if $PROGRAM_NAME == __FILE__ require "benchmark" SIZE = 512 TIMES = 10 ring = nil puts "*** Creating a #{SIZE} node ring..." puts Benchmark.measure { ring = Ring.new(SIZE) } puts "*** Sending a message around #{TIMES} times" puts Benchmark.measure { ring.run(TIMES) } end celluloid-0.18.0/examples/supervisors_and_registry.rb0000644000004100000410000000453014005764144023134 0ustar www-datawww-datarequire "celluloid/autostart" class MyActor include Celluloid attr_reader :state def initialize @state = :clean end def broken_method @state = :dirty oh_crap_im_totally_broken end end # # Using the Supervisor API directly # puts "*** Demonstrating using the Supervisor API directly" # Calling supervise directly returns the supervisor supervisor = MyActor.supervise # We can get to the current version of an actor by calling # Celluloid::Supervisor#actors. This prints ':clean' puts "We should be in a clean state now: #{supervisor.actors.first.state}" puts "Brace yourself for a crash message..." # If we call a method that crashes an actor, it will print out a debug message, # then restart an actor in a clean state begin supervisor.actors.first.broken_method rescue NameError puts "Uhoh, we crashed the actor..." end puts "The supervisor should automatically restart the actor" # By now we'll be back in a :clean state! begin puts "We should now be in a clean state again: #{supervisor.actors.first.state}" rescue Celluloid::DeadActorError # Perhaps we got ahold of the actor before the supervisor restarted it retry end # # Using the Actor Registry # This is the preferred approach and will make using DCell easier # puts "*** Demonstrating using the actor registry" # We can give our actor a name and thus avoid interacting with the supervisor MyActor.supervise as: :my_actor # Same as above, just getting the actor from a different place puts "We should be in a clean state now: #{Celluloid::Actor[:my_actor].state}" puts "Brace yourself for a crash message..." # If we call a method that crashes an actor, it will print out a debug message, # then restart an actor in a clean state begin Celluloid::Actor[:my_actor].broken_method rescue NameError puts "Uhoh, we crashed the actor..." end puts "The supervisor should automatically restart the actor" # By now we'll be back in a :clean state! begin puts "We should now be in a clean state again: #{Celluloid::Actor[:my_actor].state}" rescue Celluloid::DeadActorError # Perhaps we got ahold of the actor before the supervisor restarted it # Don't want to catch Celluloid::DeadActorError all over the place? If this # code were in a supervised Celluloid::Actor itself, the supervisor would # catch Celluloid::DeadActorError and automatically restart this actor retry end celluloid-0.18.0/examples/basic_usage.rb0000755000004100000410000000334214005764144020226 0ustar www-datawww-data#!/usr/bin/env ruby $LOAD_PATH.push File.expand_path("../lib", __dir__) require "celluloid/autostart" class Counter # This is all you have to do to turn any Ruby class into one which creates # Celluloid actors instead of normal objects include Celluloid # Now just define methods like you ordinarily would attr_reader :count def initialize @count = 0 end def increment(n = 1) @count += n end end # Create objects just like you normally would. 'actor' is now a proxy object # which talks to a Celluloid actor running in its own thread actor = Counter.new # The proxy obeys normal method invocation the way we'd expect. This prints 0 p actor.count # This increments @count by 1 and prints 1 p actor.increment # By using actor.async, you can make calls asynchronously. This immediately # requests execution of method by sending a message, and we have no idea # whether or not that request will actually complete because we don't wait # for a response. Async calls immediately return nil regardless of how long # the method takes to execute. Therefore, this will print nil. p actor.async.increment 41 # In practice, the asynchronous call made above will increment the count before # we get here. However, do not rely on this behavior! Asynchronous methods are # inherently uncoordinated. If you need to coordinate asynchronous activities, # you will need to use futures or FSMs. See the corresponding examples for those. # Signals can also be used to coordinate asynchronous activities. # # The following line could possibly print either 1 or 42, depending on if the # asynchronous call above completed. In practice, it prints 42 on all Ruby # implementations because the asynchronous call above will always execute p actor.count celluloid-0.18.0/examples/timers.rb0000755000004100000410000000266614005764144017274 0ustar www-datawww-data#!/usr/bin/env ruby $LOAD_PATH.push File.expand_path("../lib", __dir__) require "celluloid" class TimerExample include Celluloid attr_reader :fired, :timer def initialize @fired = false @timer = after(3) { puts "Timer fired!"; @fired = true } end end # # Basic timer example # actor = TimerExample.new # The timer hasn't fired yet, so this should be false puts "Timer hasn't fired yet, so this should be false: #{actor.fired}" # Even if we wait a second, it still hasn't fired sleep 1 puts "Timer still shouldn't have fired yet: #{actor.fired}" # Wait until after the timer should've fired sleep 2.1 puts "Timer should've fired now: #{actor.fired}" # # Cancelling timers # actor = TimerExample.new # The timer hasn't fired yet, so this should be false puts "Timer hasn't fired yet, so this should be false: #{actor.fired}" # Cancel the timer, which should prevent it from firing actor.timer.cancel # Wait until after the timer should've fired sleep 3.1 puts "Timer shouldn't have fired because we cancelled it: #{actor.fired}" class RepeatingTimerExample include Celluloid def initialize @sheep = 0 end def count_sheep print "<#{self.class.name}> Counting sheep to go to sleep: " @timer = every(0.1) do @sheep += 1 print @sheep, " " end end def stop_counting @timer.cancel end end sleepy_actor = RepeatingTimerExample.new sleepy_actor.count_sheep sleep 1 sleepy_actor.stop_counting celluloid-0.18.0/examples/configurations.rb0000644000004100000410000000363514005764144021015 0ustar www-datawww-datarequire "celluloid" puts "Use Supervision::Configuration objects!" class Hello include Celluloid finalizer :ceasing def initialize(to) @to = to puts "Created Hello #{@to}" end def ceasing puts "Hello #{@to} go buhbye" end end class FooBar include Celluloid finalizer :ceasing def initialize(i = 0) @i = i puts "Created FooBar: #{@i}" end def ceasing puts "FooBar FooBar: #{@i} go buhbye" end end puts "\nInstantiated in bulk, using #deploy" config = Celluloid::Supervision::Configuration.define([ { type: FooBar, as: :foobar }, { type: Hello, as: :hello, args: ["World"] } ]) config.deploy puts "...shut it down" config.shutdown puts "\nInstantiated in bulk, using .deploy" config = Celluloid::Supervision::Configuration.deploy([ { type: FooBar, as: :foobar, args: [1] }, { type: Hello, as: :hello, args: ["World"] } ]) puts "...shut it down" config.shutdown puts "\nInstantiated two actors individually, using a local configuration object" config = Celluloid::Supervision::Configuration.new config.define type: FooBar, as: :foobar11, args: [11] config.define type: FooBar, as: :foobar33, args: [33] config.deploy puts "Instantiated another, which starts automatically." puts "... using the local configuration object" puts "... using a lazy loaded argument" config.add type: Hello, as: :hello, args: -> { "Spinning World" } config.shutdown puts "\nReuse our last configuration!" config.deploy puts "...shut it down" config.shutdown puts "Thinking for 4 seconds, and 4 seconds only." sleep 4 puts "Use Supervision::Configuration objects. Really!" celluloid-0.18.0/examples/futures.rb0000755000004100000410000000230014005764144017447 0ustar www-datawww-data#!/usr/bin/env ruby $LOAD_PATH.push File.expand_path("../lib", __dir__) require "celluloid/autostart" require "digest/sha2" class Hasher include Celluloid def initialize(secret) @hash = Digest::SHA2.hexdigest(secret) end # Add some data into our hash. This demonstrates a non-trivial computation # of the same sort as, say, calculating Fibonacci numbers. Since Celluloid # uses several threads, doing something like this won't grind our entire # application to a halt def add(data, n = 100_000) string = @hash + data n.times { string = Digest::SHA2.hexdigest(string) } @hash = string end end # Create the hasher hasher = Hasher.new("super secret initialization data") # Ask the hasher to perform a complex computation. However, since we're using # a future, this doesn't block the current thread future = hasher.future.add("some data to be hashed") # We've kicked off the hasher, but this thread can continue performing other # activities while the hasher runs in the background puts "The hasher is now running, but this thread is free to do whatever it wants" # Now let's ask for the return value from the hasher puts "Getting the hasher's return value... " p future.value celluloid-0.18.0/examples/simple_pmap.rb0000755000004100000410000000050214005764144020262 0ustar www-datawww-data#!/usr/bin/env ruby $LOAD_PATH.push File.expand_path("../lib", __dir__) require "celluloid/autostart" module Enumerable # Simple parallel map using Celluloid::Futures def pmap(&block) futures = map { |elem| Celluloid::Future.new(elem, &block) } futures.map(&:value) end end p 100.times.pmap { |n| n * 2 } celluloid-0.18.0/examples/stack.rb0000755000004100000410000000176714005764144017077 0ustar www-datawww-data#!/usr/bin/env ruby $LOAD_PATH.push File.expand_path("../lib", __dir__) require "celluloid/autostart" # This example builds on basic_usage.rb to show two things about #async: the # (new) fluent API and the preservation of causality order. class Stack include Celluloid attr_reader :ary def initialize @ary = [] end def push(x) @ary.push x end alias << push def pop @ary.pop end def show p @ary end end st = Stack.new # Schedule three calls to #push some integers on the stack. They will execute # in order because the calls originated as a sequence of method calls in a # single thread. st.async << 1 << 2 << 3 # Schedule a call to show the stack after the three push calls execute. st.async.show # Schedule three calls to #pop from the stack. st.async.pop.pop.pop # The next (non-async) call is guaranteed to execute after methods previously # scheduled in this thread. The causal order of calls (order as requested) is # preserved in the execution order. st.show celluloid-0.18.0/lib/0000755000004100000410000000000014005764144014357 5ustar www-datawww-datacelluloid-0.18.0/lib/celluloid/0000755000004100000410000000000014005764144016333 5ustar www-datawww-datacelluloid-0.18.0/lib/celluloid/autostart.rb0000644000004100000410000000036114005764144020706 0ustar www-datawww-datarequire "celluloid" Celluloid.boot # !!! DO NOT INTRODUCE ADDITIONAL GLOBAL VARIABLES !!! # rubocop:disable Style/GlobalVars Celluloid.register_shutdown unless defined?($CELLULOID_TEST) && $CELLULOID_TEST # rubocop:enable Style/GlobalVars celluloid-0.18.0/lib/celluloid/supervision/0000755000004100000410000000000014005764144020721 5ustar www-datawww-datacelluloid-0.18.0/lib/celluloid/supervision/validation.rb0000644000004100000410000000243014005764144023377 0ustar www-datawww-datamodule Celluloid module Supervision class Configuration class << self def valid?(configuration, fails = false) parameters(:mandatory).each do |k| unless configuration.key? k if fails raise Error::Incomplete, "Missing `:#{k}` in supervision configuration." else return false end end end arity.each do |klass, args| next if configuration[args].is_a? Proc __a = configuration[args] && configuration[args].count || 0 __arity = configuration[klass].allocate.method(:initialize).arity unless (__arity < 0 && __a >= __arity.abs - 1) || __a == __arity.abs if fails raise ArgumentError, "#{__a} vs. #{__arity}" else return false end end end true end def options(config = {}, options = {}) configuration = config.merge(options) return configuration if configuration.is_a? Configuration configuration[:configuration] = Container::Behavior.configure(configuration) valid?(configuration, true) configuration end end end end end celluloid-0.18.0/lib/celluloid/supervision/version.rb0000644000004100000410000000011614005764144022731 0ustar www-datawww-datamodule Celluloid module Supervision VERSION = "0.20.6".freeze end end celluloid-0.18.0/lib/celluloid/supervision/container/0000755000004100000410000000000014005764144022703 5ustar www-datawww-datacelluloid-0.18.0/lib/celluloid/supervision/container/instance.rb0000644000004100000410000000760414005764144025043 0ustar www-datawww-datamodule Celluloid # Supervise collections of actors as a group module Supervision class Container class Instance attr_reader :name, :actor # @option options [#call, Object] :args ([]) arguments array for the # actor's constructor (lazy evaluation if it responds to #call) def initialize(configuration = {}) @type = configuration.delete(:type) @registry = configuration.delete(:registry) @branch = configuration.delete(:branch) || :services @configuration = configuration # allows injections inside initialize, start, and restart @injections = configuration.delete(:injections) || {} invoke_injection(:before_initialize) # Stringify keys :/ # de @configuration = configuration.each_with_object({}) { |(k,v), h| h[k.to_s] = v } @name = @configuration[:as] @block = @configuration[:block] @args = prepare_args(@configuration[:args]) @method = @configuration[:method] || "new_link" add_accessors invoke_injection(:after_initialize) start end def start invoke_injection(:before_start) @actor = @type.send(@method, *@args, &@block) @registry.add(@name, @actor, @branch) if @name invoke_injection(:after_start) rescue Celluloid::TaskTimeout => ex Internals::Logger.error("TaskTimeout at start of supervised instance of #{@type}") raise ex # TODO: Implement timeout/retry(?) rescue => ex Internals::Logger.error("Error ( #{ex.class} ) at start of supervised instance of #{@type}") raise ex end def restart # no need to reset @actor, as this is called in an `exclusive {}` block # @actor = nil # cleanup invoke_injection(:before_restart) start invoke_injection(:after_restart) end def terminate @actor.terminate if @actor cleanup rescue DeadActorError end def cleanup @registry.delete(@name) if @name end private def add_accessors remove_accessors if @configuration[:accessors].is_a? Array # TODO: Decide which level to keep, and only keep that. # Do we provide access by Celluloid.accessor # Do we provide access by Celluloid.actor_system.accessor @configuration[:accessors].each do |name| Celluloid.instance_exec(@configuration[:as], name) do |actor, _where| define_method(name) do Celluloid.actor_system[actor] end end Celluloid::Actor::System.instance_exec(@configuration[:as], name) do |actor, _where| define_method(name) do Celluloid.actor_system[actor] end end end end end def remove_accessors if @configuration[:accessors].is_a? Array @configuration[:accessors].each do |name| Celluloid.instance_eval do remove_method(name) rescue nil # avoid warnings in tests end Celluloid::Actor::System.instance_eval do remove_method(name) rescue nil # avoid warnings in tests end end end end def invoke_injection(name) return unless @injections block = @injections[name] instance_eval(&block) if block.is_a? Proc end # Executes args if it has the method #call, and converts the return # value to an Array. Otherwise, it just converts it to an Array. def prepare_args(args) args = args.call if args.respond_to?(:call) Array(args) end end end end end celluloid-0.18.0/lib/celluloid/supervision/container/behavior/0000755000004100000410000000000014005764144024502 5ustar www-datawww-datacelluloid-0.18.0/lib/celluloid/supervision/container/behavior/tree.rb0000644000004100000410000000125014005764144025764 0ustar www-datawww-datamodule Celluloid module Supervision class Container class Tree include Behavior identifier! :supervises, :supervise configuration do if @configuration[:supervise].is_a? Array @supervisor = @configuration.dup @branch = @configuration.fetch(:branch, @configuration[:as]) @configuration.delete(Behavior.parameter(:supervise, @configuration)) elsif @configuration[:supervise].is_a?(Celluloid::Supervision::Configuration) @configuration else raise ArgumentError, "No actors given to Tree to supervise." end end end end end end celluloid-0.18.0/lib/celluloid/supervision/container/behavior/pool.rb0000644000004100000410000000400514005764144025777 0ustar www-datawww-datarequire "set" module Celluloid module ClassMethods extend Forwardable def_delegators :"Celluloid::Supervision::Container::Pool", :pooling_options # Create a new pool of workers. Accepts the following options: # # * size: how many workers to create. Default is worker per CPU core # * args: array of arguments to pass when creating a worker # def pool(config = {}, &block) _ = Celluloid.supervise(pooling_options(config, block: block, actors: self)) _.actors.last end # Same as pool, but links to the pool manager def pool_link(klass, config = {}, &block) Supervision::Container::Pool.new_link(pooling_options(config, block: block, actors: klass)) end end module Supervision class Container extend Forwardable def_delegators :"Celluloid::Supervision::Container::Pool", :pooling_options def pool(klass, config = {}, &block) _ = supervise(pooling_options(config, block: block, actors: klass)) _.actors.last end class Instance attr_accessor :pool, :pool_size end class << self # Register a pool of actors to be launched on group startup def pool(klass, config, &block) blocks << lambda do |container| container.pool(klass, config, &block) end end end class Pool include Behavior class << self def pooling_options(config = {}, mixins = {}) combined = { type: Celluloid::Supervision::Container::Pool }.merge(config).merge(mixins) combined[:args] = [%i[block actors size args].each_with_object({}) do |p, e| e[p] = combined.delete(p) if combined[p] end] combined end end identifier! :size, :pool configuration do @supervisor = Container::Pool @method = "pool_link" @pool = true @pool_size = @configuration[:size] @configuration end end end end end celluloid-0.18.0/lib/celluloid/supervision/container/behavior.rb0000644000004100000410000000552114005764144025032 0ustar www-datawww-datamodule Celluloid module Supervision class Container module Behavior @@injections = {} # Hash of Class => Hash of Injections @@behaviors = {} # Hash of identifying symbol parameter => Class module Error class Mutant < Celluloid::Error; end end class << self def included(klass) klass.send :extend, ClassMethods end def injections @@injections end def [](identifier) @@behaviors[identifier] end def []=(identifier, behavior) @@behaviors[identifier] = behavior end def parameter(identifier, options) found = nil p = Configuration.aliases.each_with_object([identifier]) { |(a, i), invoke| invoke << a if i == identifier; } case p.count { |parameter| found = parameter; options.key?(parameter) } when 1 found when 0 else raise Error::Mutant, "More than one kind of identifiable behavior parameter." end end # Beware of order. There may be multiple behavior injections, but their order is not determined ( yet ) # Right now, something like a pool-coordinator-tree supervisor mutant are absolutely expected to crash. # Therefore, sorry Professor X -- we kill every Mutant. On sight, no questions asked. Zero mutant love. def configure(options) behavior = nil injection = nil @@behaviors.map do |identifier, injector| if identifier = parameter(identifier, options) if behavior raise Error::Mutant, "More than one type of behavior expected." else if @@injections[injector].include?(:configuration) injection = @@injections[behavior = injector][:configuration] options[:behavior] ||= behavior end end end end options[:type] ||= behavior injection || proc { @configuration } end module ClassMethods def identifier!(identifier, *aliases) Behavior[identifier] = self Configuration.parameter! :plugins, identifier aliases.each do |aliased| Configuration.alias! aliased, identifier end Configuration.save_defaults end def behavior_injections Behavior.injections[self] ||= {} end Configuration::INJECTIONS.each do |point| define_method(point) do |&injector| behavior_injections[point] = injector end end end end end end end end celluloid-0.18.0/lib/celluloid/supervision/container/pool.rb0000644000004100000410000001333414005764144024205 0ustar www-datawww-datamodule Celluloid module Supervision class Container # Manages a fixed-size pool of actors # Delegates work (i.e. methods) and supervises actors # Don't use this class directly. Instead use MyKlass.pool class Pool include Celluloid trap_exit :__crash_handler__ finalizer :__shutdown__ attr_reader :size, :actors def initialize(options = {}) @idle = [] @busy = [] @klass = options[:actors] @actors = Set.new @mutex = Mutex.new @size = options[:size] || [Celluloid.cores || 2, 2].max @args = options[:args] ? Array(options[:args]) : [] # Do this last since it can suspend and/or crash @idle = @size.times.map { __spawn_actor__ } end def __shutdown__ return unless defined?(@actors) && @actors # TODO: these can be nil if initializer crashes terminators = @actors.map do |actor| begin actor.future(:terminate) rescue DeadActorError end end terminators.compact.each { |terminator| terminator.value rescue nil } end def _send_(method, *args, &block) actor = __provision_actor__ begin actor._send_ method, *args, &block rescue DeadActorError # if we get a dead actor out of the pool wait :respawn_complete actor = __provision_actor__ retry rescue ::Exception => ex abort ex ensure if actor.alive? @idle << actor @busy.delete actor # Broadcast that actor is done processing and # waiting idle signal :actor_idle end end end def name _send_ @mailbox, :name end def is_a?(klass) _send_ :is_a?, klass end def kind_of?(klass) _send_ :kind_of?, klass end def methods(include_ancestors = true) _send_ :methods, include_ancestors end def to_s _send_ :to_s end def inspect _send_ :inspect end def size=(new_size) new_size = [0, new_size].max if new_size > size delta = new_size - size delta.times { @idle << __spawn_actor__ } else (size - new_size).times do actor = __provision_actor__ unlink actor @busy.delete actor @actors.delete actor actor.terminate end end @size = new_size end def busy_size @mutex.synchronize { @busy.length } end def idle_size @mutex.synchronize { @idle.length } end def __idle?(actor) @mutex.synchronize { @idle.include? actor } end def __busy?(actor) @mutex.synchronize { @busy.include? actor } end def __busy @mutex.synchronize { @busy } end def __idle @mutex.synchronize { @idle } end def __state(actor) return :busy if __busy?(actor) return :idle if __idle?(actor) :missing end # Instantiate an actor, add it to the actor Set, and return it def __spawn_actor__ actor = @klass.new_link(*@args) @mutex.synchronize { @actors.add(actor) } @actors.add(actor) actor end # Provision a new actor ( take it out of idle, move it into busy, and avail it ) def __provision_actor__ Task.current.guard_warnings = true @mutex.synchronize do while @idle.empty? # Wait for responses from one of the busy actors response = exclusive { receive { |msg| msg.is_a?(Internals::Response) } } Thread.current[:celluloid_actor].handle_message(response) end actor = @idle.shift @busy << actor actor end end # Spawn a new worker for every crashed one def __crash_handler__(actor, reason) @busy.delete actor @idle.delete actor @actors.delete actor return unless reason @idle << __spawn_actor__ signal :respawn_complete end def respond_to?(meth, include_private = false) # NOTE: use method() here since this class # shouldn't be used directly, and method() is less # likely to be "reimplemented" inconsistently # with other Object.*method* methods. found = method(meth) if include_private found ? true : false else if found.is_a?(UnboundMethod) found.owner.public_instance_methods.include?(meth) || found.owner.protected_instance_methods.include?(meth) else found.receiver.public_methods.include?(meth) || found.receiver.protected_methods.include?(meth) end end rescue NameError false end def method_missing(method, *args, &block) if respond_to?(method) _send_ method, *args, &block else super end end # Since Pool allocates worker objects only just before calling them, # we can still help Celluloid::Call detect passing invalid parameters to # async methods by checking for those methods on the worker class def method(meth) super rescue NameError @klass.instance_method(meth.to_sym) end end end end end celluloid-0.18.0/lib/celluloid/supervision/container/injections.rb0000644000004100000410000000015414005764144025375 0ustar www-datawww-datamodule Celluloid module Supervision class Container class Injection end end end end celluloid-0.18.0/lib/celluloid/supervision/supervise.rb0000644000004100000410000000154114005764144023274 0ustar www-datawww-data# collect together all instances of the `supervise` method module Celluloid class << self def supervise(config = {}, &block) supervisor = Supervision.router(config) supervisor.supervise(config, &block) end end module ClassMethods def supervise(config = {}, &block) Celluloid.supervise(config.merge(type: self), &block) end end module Supervision class << self def router(_config = {}) # TODO: Actually route. Celluloid.services # for now, hardcode .services end end class Container class << self def supervise(config, &block) blocks << lambda do |container| container.add(config, &block) end end end def supervise(config, &block) add(Configuration.options(config, block: block)) end end end end celluloid-0.18.0/lib/celluloid/supervision/configuration.rb0000644000004100000410000001105114005764144024113 0ustar www-datawww-datamodule Celluloid module Supervision class Configuration class << self def deploy(options = {}) define(options).deploy end def define(options = {}) new(options) end end extend Forwardable def_delegators :current_instance, :delete, :key?, :set, :get, :[], :[]=, :injection!, :injections! attr_accessor :instances def initialize(options = {}) @instances = [Instance.new] @branch = :services @i = 0 # incrementer of instances in this branch resync_accessors @configuration = options if options.is_a? Hash options[:configuration] ||= Container::Behavior.configure(options) @configuration = instance_eval(&options[:configuration]) @supervisor ||= @configuration.fetch(:supervisor, :"Celluloid.services") end @supervisor ||= :"Celluloid.services" define(@configuration) if (@configuration.is_a?(Hash) || @configuration.is_a?(Array)) && @configuration.any? end def provider @provider ||= if @supervisor.is_a? Hash @supervisor[:type].run!(@supervisor) elsif @supervisor.is_a? Symbol @supervisor = Object.module_eval(@supervisor.to_s) provider elsif @supervisor.is_a? Class @supervisor.run! elsif @supervisor.respond_to? :supervise @supervisor else raise Error::InvalidSupervisor end end def deploy(options = {}) define(options) if options.any? @instances.each do |instance| provider.add instance.merge(branch: @branch) end provider end def count @instances.count end def each(&block) @instances.each(&block) end def resync_accessors # methods for setting and getting the usual defaults Configuration.parameters(:mandatory, :optional, :plugins, :meta).each do |key| [:"#{key}!", :"#{key}="].each do |m| self.class.instance_eval do remove_method :"#{m}" rescue nil # avoid warnings in tests define_method(m) { |p| current_instance.send(m, p) } end end [:"#{key}?", :"#{key}"].each do |m| self.class.instance_eval do remove_method :"#{m}" rescue nil # avoid warnings in tests define_method(m) { current_instance.send(m) } end end end Configuration.aliases.each do |_alias, _original| ["!", :"=", :"?", :""]. each do |m| self.class.instance_eval do remove_method :"#{_alias}#{m}" rescue nil # avoid warnings in tests alias_method :"#{_alias}#{m}", :"#{_original}#{m}" end end end end def merge!(values) if values.is_a?(Configuration) || values.is_a?(Hash) current_instance.merge!(values) else raise Error::Invalid end end def merge(values) if values.is_a?(Configuration) || values.is_a?(Hash) current_instance.merge(values) else raise Error::Invalid end end def export return current_instance.to_hash if @i == 0 @instances.map(&:export) end def include?(name) @instances.map(&:name).include? name end def define(configuration, fail = false) if configuration.is_a? Array configuration.each { |c| define(c, fail) } else unless include? configuration[:as] begin current_instance.define(configuration, fail) rescue Error::AlreadyDefined increment retry end end end self end def increment @i += 1 end alias another increment def add(options) define(options) provider.supervise options if Configuration.valid? options end def shutdown @provider.shutdown end private def current_instance @instances[@i] ||= Instance.new end def invoke_injection(_point) # de puts "injection? #{point}" end end end end celluloid-0.18.0/lib/celluloid/supervision/container.rb0000644000004100000410000000710214005764144023230 0ustar www-datawww-datamodule Celluloid # Supervise actor instances in a container. module Supervision class Container include Celluloid trap_exit :restart_actor class << self def define(options) Configuration.define(top(options)) end def deploy(options) Configuration.deploy(top(options)) end def top(options) { as: options.delete(:as), type: (options.delete(:type) || self), branch: (options.delete(:branch) || :services), supervise: options.delete(:supervise) || [] } end # Actors or sub-applications to be supervised def blocks @blocks ||= [] end # Start this application (and watch it with a supervisor) def run!(options = {}) container = new(options) do |g| blocks.each do |block| block.call(g) end end container end # Run the application in the foreground with a simple watchdog def run(options = {}) loop do supervisor = run!(options) # Take five, toplevel supervisor sleep 5 while supervisor.alive? # Why 5? Internals::Logger.error "!!! Celluloid::Supervision::Container #{self} crashed. Restarting..." end end end finalizer :finalize attr_accessor :registry # Start the container. def initialize(options = {}) options = { registry: options } if options.is_a? Internals::Registry @state = :initializing @actors = [] # instances in the container @registry = options.delete(:registry) || Celluloid.actor_system.registry @branch = options.delete(:branch) || :services yield current_actor if block_given? end execute_block_on_receiver :initialize, :supervise, :supervise_as def add(configuration) Configuration.valid?(configuration, true) @actors << Instance.new(configuration.merge(registry: @registry, branch: @branch)) @state = :running add_accessors configuration Actor.current end def add_accessors(configuration) if configuration[:as] unless methods.include? configuration[:as] self.class.instance_eval do define_method(configuration[:as]) do @registry[configuration[:as]] end end end end end def remove_accessors; end def remove(actor) actor = Celluloid::Actor[actor] if actor.is_a? Symbol instance = find(actor) instance.terminate if instance end def actors @actors.map(&:actor) end def find(actor) @actors.find do |instance| instance.actor == actor end end def [](actor_name) @registry[actor_name] end # Restart a crashed actor def restart_actor(actor, reason) return if @state == :shutdown instance = find(actor) raise "a container instance went missing. This shouldn't be!" unless instance if reason exclusive { instance.restart } else instance.cleanup @actors.delete(instance) end end def shutdown @state = :shutdown finalize end private def finalize if @actors @actors.reverse_each do |instance| instance.terminate @actors.delete(instance) end end end end end end celluloid-0.18.0/lib/celluloid/supervision/constants.rb0000644000004100000410000000570114005764144023265 0ustar www-datawww-datamodule Celluloid module Supervision # TODO: Do not hard-code. Allow configurable values. INSTANCE_RETRY_WAIT = 3 INSTANCE_RETRY_LIMIT = 5 module Error class NoPublicService < Celluloid::Error; end end class Configuration module Error class AlreadyDefined < Celluloid::Error; end class InvalidSupervisor < Celluloid::Error; end class InvalidValues < Celluloid::Error; end class Incomplete < Celluloid::Error; end class Invalid < Celluloid::Error; end end # Using class variable so that parameters can be added by plugins. @@parameters = { mandatory: [:type], optional: %i[ as args block ], # TODO: Move these into Behaviors. plugins: [ # de :size, # Supervision::Pool # de :routers, # Supervision::Coordinator # de :supervises # Supervision::Tree ], meta: %i[ registry branch method ] } @@arity = { type: :args } @@aliases = { name: :as, kind: :type, # de :pool => :size, # TODO: Move into Behaviors. # de :supervise => :supervises } @@defaults = {} class << self def save_defaults @@defaults = { parameters: @@parameters.each_with_object({}) { |(k, v), p| p[k] = v.dup; }, aliases: @@aliases.dup, arity: @@arity.dup } end def resync_parameters @@parameters = @@defaults[:parameters].each_with_object({}) { |(k, v), p| p[k] = v.dup; } @@aliases = @@defaults[:aliases].dup @@arity = @@defaults[:arity].dup end def parameters(*args) args.inject([]) { |parameters, p| parameters += @@parameters[p]; parameters } end def parameter!(key, value) @@parameters[key] << value unless @@parameters[key].include? value end def arity @@arity end def arity!(key, value) @@arity[key] = value end def aliases @@aliases end def alias!(aliased, original) @@aliases[aliased] = original end end save_defaults resync_parameters # This was originally added for `#pool` and `PoolManager # `:before_initialize` was added to allow detecting `:size => N` # and turning that into a pool. Other uses could be for `coordinator` # appointing a `router` by detecting `:routers => N`, and many other uses. ################ These are applied inside Supervision::Member ################ REMOVE_AT_EXPORT = %i[ configuration behavior ].freeze INJECTIONS = %i[ configuration before_initialization after_initialization before_start before_restart ].freeze end end end celluloid-0.18.0/lib/celluloid/supervision/service.rb0000644000004100000410000000117614005764144022713 0ustar www-datawww-datamodule Celluloid module Supervision module Service class Root < Container class << self def define super({ supervise: Celluloid.actor_system.root_configuration, as: :root_supervisor, accessors: [:root], branch: :root, type: self }) end def deploy(instances) super(supervise: instances, branch: :root, as: :root, type: self) end end def provider Celluloid.root_services end end class Public < Container; end end end end celluloid-0.18.0/lib/celluloid/supervision/configuration/0000755000004100000410000000000014005764144023570 5ustar www-datawww-datacelluloid-0.18.0/lib/celluloid/supervision/configuration/instance.rb0000644000004100000410000000641514005764144025727 0ustar www-datawww-datamodule Celluloid module Supervision class Configuration class Instance attr_accessor :configuration def initialize(configuration = {}) @state = :initializing # :ready resync_accessors @configuration = configuration define(configuration) if configuration.any? end def export @configuration.reject { |k, _v| REMOVE_AT_EXPORT.include? k } end def ready?(fail = false) unless @state == :ready @state = :ready if Configuration.valid? @configuration, fail end @state == :ready end def define(instance, fail = false) raise Configuration::Error::AlreadyDefined if ready? fail invoke_injection(:before_configuration) @configuration = Configuration.options(instance) ready? end def injection!(key, proc) @configuration[:injections] ||= {} @configuration[:injections][key] = proc end def injections!(_procs) @configuration[:injections] = proces end def resync_accessors # methods for setting and getting the usual defaults Configuration.parameters(:mandatory, :optional, :plugins, :meta).each do |key| self.class.instance_eval do remove_method :"#{key}!" rescue nil # avoid warnings in tests define_method(:"#{key}!") { |value| @configuration[key] = value } end self.class.instance_eval do remove_method :"#{key}=" rescue nil # avoid warnings in tests define_method(:"#{key}=") { |value| @configuration[key] = value } end self.class.instance_eval do remove_method :"#{key}?" rescue nil # avoid warnings in tests define_method(:"#{key}?") { !@configuration[key].nil? } end self.class.instance_eval do remove_method :"#{key}" rescue nil # avoid warnings in tests define_method(:"#{key}") { @configuration[key] } end end Configuration.aliases.each do |_alias, _original| ["!", :"=", :"?", :""]. each do |m| self.class.instance_eval do remove_method :"#{_alias}#{m}" rescue nil # avoid warnings in tests alias_method :"#{_alias}#{m}", :"#{_original}#{m}" end end end true end def merge!(values) @configuration = @configuration.merge(values) end def merge(values) if values.is_a? Configuration @configuration.merge(values.configuration) elsif values.is_a? Hash @configuration.merge(values) else raise Error::Invalid end end def key?(k) @configuration.key?(k) end def set(key, value) @configuration[key] = value end alias []= set def get(key) @configuration[key] end alias [] get def delete(k) @configuration.delete(k) end private def invoke_injection(_point) # de puts "injection? #{point}" end end end end end celluloid-0.18.0/lib/celluloid/supervision/configuration/injections.rb0000644000004100000410000000016014005764144026257 0ustar www-datawww-datamodule Celluloid module Supervision class Configuration class Injection end end end end celluloid-0.18.0/lib/celluloid/cell.rb0000644000004100000410000000571614005764144017610 0ustar www-datawww-datamodule Celluloid OWNER_IVAR = :@celluloid_owner # reference to owning actor # Wrap the given subject with an Cell class Cell class ExitHandler def initialize(behavior, subject, method_name) @behavior = behavior @subject = subject @method_name = method_name end def call(event) @behavior.task(:exit_handler, @method_name) do @subject.send(@method_name, event.actor, event.reason) end end end def initialize(subject, options, actor_options) @actor = Actor.new(self, actor_options) @subject = subject @receiver_block_executions = options[:receiver_block_executions] @exclusive_methods = options[:exclusive_methods] @finalizer = options[:finalizer] @subject.instance_variable_set(OWNER_IVAR, @actor) if exit_handler_name = options[:exit_handler_name] @actor.exit_handler = ExitHandler.new(self, @subject, exit_handler_name) end @actor.handle(Call) do |message| invoke(message) end @actor.handle(Call::Block) do |message| task(:invoke_block) { message.dispatch } end @actor.handle(Internals::Response::Block, Internals::Response, &:dispatch) @actor.start @proxy = (options[:proxy_class] || Proxy::Cell).new(@actor.mailbox, @actor.proxy, @subject.class.to_s) end attr_reader :proxy, :subject def self.dispatch proc do |subject| subject[:call].dispatch(subject[:subject]) subject[:call] = nil subject[:subject] = nil end end def invoke(call) meth = call.method meth = call.arguments.first if meth == :__send__ if @receiver_block_executions && meth call.execute_block_on_receiver if @receiver_block_executions.include?(meth.to_sym) end task(:call, meth, { call: call, subject: @subject }, dangerous_suspend: meth == :initialize, &Cell.dispatch) end def task(task_type, method_name = nil, subject = nil, meta = nil, &_block) meta ||= {} meta[:method_name] = method_name @actor.task(task_type, meta) do if @exclusive_methods && method_name && @exclusive_methods.include?(method_name.to_sym) Celluloid.exclusive { yield subject } else yield subject end end end def self.shutdown proc do |subject| begin subject[:subject].__send__(subject[:call]) rescue => ex Internals::Logger.crash("#{subject[:subject].class} finalizer crashed!", ex) end subject[:call] = nil subject[:subject] = nil end end # Run the user-defined finalizer, if one is set def shutdown return unless @finalizer && @subject.respond_to?(@finalizer, true) task(:finalizer, @finalizer, { call: @finalizer, subject: @subject }, dangerous_suspend: true, &Cell.shutdown) end end end celluloid-0.18.0/lib/celluloid/actor.rb0000644000004100000410000002443614005764144020001 0ustar www-datawww-datarequire "timers" module Celluloid # Actors are Celluloid's concurrency primitive. They're implemented as # normal Ruby objects wrapped in threads which communicate with asynchronous # messages. class Actor attr_reader :behavior, :proxy, :tasks, :links, :mailbox, :thread, :name, :timers attr_writer :exit_handler class << self extend Forwardable def_delegators :"Celluloid.actor_system", :[], :[]=, :delete, :registered, :clear_registry # Obtain the current actor def current actor = Thread.current[:celluloid_actor] raise NotActorError, "not in actor scope" unless actor actor.behavior_proxy end # Obtain the name of the current actor def registered_name actor = Thread.current[:celluloid_actor] raise NotActorError, "not in actor scope" unless actor actor.name end # Invoke a method on the given actor via its mailbox def call(mailbox, meth, *args, &block) proxy = Proxy::Sync.new(mailbox, "UnknownClass") proxy.method_missing(meth, *args, &block) end # Invoke a method asynchronously on an actor via its mailbox def async(mailbox, meth, *args, &block) proxy = Proxy::Async.new(mailbox, "UnknownClass") proxy.method_missing(meth, *args, &block) end # Call a method asynchronously and retrieve its value later def future(mailbox, meth, *args, &block) proxy = Proxy::Future.new(mailbox, "UnknownClass") proxy.method_missing(meth, *args, &block) end # Obtain all running actors in the system def all Celluloid.actor_system.running end # Watch for exit events from another actor def monitor(actor) raise NotActorError, "can't link outside actor context" unless Celluloid.actor? Thread.current[:celluloid_actor].linking_request(actor, :link) end # Stop waiting for exit events from another actor def unmonitor(actor) raise NotActorError, "can't link outside actor context" unless Celluloid.actor? Thread.current[:celluloid_actor].linking_request(actor, :unlink) end # Link to another actor def link(actor) monitor actor Thread.current[:celluloid_actor].links << actor end # Unlink from another actor def unlink(actor) unmonitor actor Thread.current[:celluloid_actor].links.delete actor end # Are we monitoring the given actor? def monitoring?(actor) actor.links.include? Actor.current end # Are we bidirectionally linked to the given actor? def linked_to?(actor) monitoring?(actor) && Thread.current[:celluloid_actor].links.include?(actor) end unless RUBY_PLATFORM == "java" || RUBY_ENGINE == "rbx" # Forcibly kill a given actor def kill(actor) actor.thread.kill actor.mailbox.shutdown if actor.mailbox.alive? end end # Wait for an actor to terminate def join(actor, timeout = nil) actor.thread.join(timeout) actor end end def initialize(behavior, options) @behavior = behavior @actor_system = options.fetch(:actor_system) @mailbox = options.fetch(:mailbox_class, Mailbox).new @mailbox.max_size = options.fetch(:mailbox_size, nil) @task_class = options[:task_class] || Celluloid.task_class @exit_handler = method(:default_exit_handler) @exclusive = options.fetch(:exclusive, false) @timers = Timers::Group.new @tasks = Internals::TaskSet.new @links = Internals::Links.new @handlers = Internals::Handlers.new @receivers = Internals::Receivers.new(@timers) @signals = Internals::Signals.new @running = false @name = nil handle(SystemEvent) do |message| handle_system_event message end end def start @running = true @thread = Internals::ThreadHandle.new(@actor_system, :actor) do setup_thread run end @proxy = Proxy::Actor.new(@mailbox, @thread) # !!! DO NOT INTRODUCE ADDITIONAL GLOBAL VARIABLES !!! # rubocop:disable Style/GlobalVars Celluloid::Probe.actor_created(self) if $CELLULOID_MONITORING # rubocop:enable Style/GlobalVars end def behavior_proxy @behavior.proxy end def setup_thread Thread.current[:celluloid_actor] = self Thread.current[:celluloid_mailbox] = @mailbox end # Run the actor loop def run while @running begin @timers.wait do |interval| interval = 0 if interval && interval < 0 if message = @mailbox.check(interval) handle_message(message) break unless @running end end rescue MailboxShutdown @running = false rescue MailboxDead # TODO: not tests (but fails occasionally in tests) @running = false end end shutdown rescue ::Exception => ex handle_crash(ex) raise unless ex.is_a?(StandardError) || ex.is_a?(Celluloid::Interruption) end # Terminate this actor def terminate @running = false end # Perform a linking request with another actor def linking_request(receiver, type) Celluloid.exclusive do receiver.mailbox << LinkingRequest.new(Actor.current, type) system_events = [] Timers::Wait.for(LINKING_TIMEOUT) do |remaining| begin message = @mailbox.receive(remaining) do |msg| msg.is_a?(LinkingResponse) && msg.actor.mailbox.address == receiver.mailbox.address && msg.type == type end rescue TaskTimeout next # IO reactor did something, no message in queue yet. end if message.instance_of? LinkingResponse # !!! DO NOT INTRODUCE ADDITIONAL GLOBAL VARIABLES !!! # rubocop:disable Style/GlobalVars Celluloid::Probe.actors_linked(self, receiver) if $CELLULOID_MONITORING # rubocop:enable Style/GlobalVars system_events.each { |ev| @mailbox << ev } return elsif message.is_a? SystemEvent # Queue up pending system events to be processed after we've successfully linked system_events << message else raise "Unexpected message type: #{message.class}. Expected LinkingResponse, NilClass, SystemEvent." end end raise TaskTimeout, "linking timeout of #{LINKING_TIMEOUT} seconds exceeded with receiver: #{receiver}" end end # Send a signal with the given name to all waiting methods def signal(name, value = nil) @signals.broadcast name, value end # Wait for the given signal def wait(name) @signals.wait name end # Register a new handler for a given pattern def handle(*patterns, &block) @handlers.handle(*patterns, &block) end # Receive an asynchronous message def receive(timeout = nil, &block) loop do message = @receivers.receive(timeout, &block) return message unless message.is_a?(SystemEvent) handle_system_event(message) end end # Schedule a block to run at the given time def after(interval, &block) @timers.after(interval) { task(:timer, &block) } end # Schedule a block to run at the given time def every(interval, &block) @timers.every(interval) { task(:timer, &block) } end def timeout(duration) bt = caller task = Task.current timer = @timers.after(duration) do exception = TaskTimeout.new("execution expired") exception.set_backtrace bt task.resume exception end yield ensure timer.cancel if timer end class Sleeper def initialize(timers, interval) @timers = timers @interval = interval end def before_suspend(task) @timers.after(@interval) { task.resume } end def wait Kernel.sleep(@interval) end end # Sleep for the given amount of time def sleep(interval) sleeper = Sleeper.new(@timers, interval) Celluloid.suspend(:sleeping, sleeper) end # Handle standard low-priority messages def handle_message(message) # !!! DO NOT INTRODUCE ADDITIONAL GLOBAL VARIABLES !!! # rubocop:disable Metrics/LineLength, Style/GlobalVars Internals::Logger.debug "Discarded message (unhandled): #{message}" if !@handlers.handle_message(message) && !@receivers.handle_message(message) && $CELLULOID_DEBUG # rubocop:enable Metrics/LineLength, Style/GlobalVars message end def default_exit_handler(event) raise event.reason if event.reason end # Handle any exceptions that occur within a running actor def handle_crash(exception) # TODO: add meta info Internals::Logger.crash("Actor crashed!", exception) shutdown ExitEvent.new(behavior_proxy, exception) rescue => ex Internals::Logger.crash("Actor#handle_crash CRASHED!", ex) end # Handle cleaning up this actor after it exits def shutdown(exit_event = ExitEvent.new(behavior_proxy)) @behavior.shutdown cleanup exit_event ensure Thread.current[:celluloid_actor] = nil Thread.current[:celluloid_mailbox] = nil end # Clean up after this actor def cleanup(exit_event) # !!! DO NOT INTRODUCE ADDITIONAL GLOBAL VARIABLES !!! # rubocop:disable Style/GlobalVars Celluloid::Probe.actor_died(self) if $CELLULOID_MONITORING # rubocop:enable Style/GlobalVars @mailbox.shutdown @links.each do |actor| actor.mailbox << exit_event if actor.mailbox.alive? end tasks.to_a.each do |task| begin task.terminate rescue DeadTaskError # TODO: not tested (failed on Travis) end end rescue => ex # TODO: metadata Internals::Logger.crash("CLEANUP CRASHED!", ex) end # Run a method inside a task unless it's exclusive def task(task_type, meta = nil) @task_class.new(task_type, meta) do if @exclusive Celluloid.exclusive { yield } else yield end end.resume end end end celluloid-0.18.0/lib/celluloid/version.rb0000644000004100000410000000006114005764144020342 0ustar www-datawww-datamodule Celluloid VERSION = "0.18.0".freeze end celluloid-0.18.0/lib/celluloid/logging/0000755000004100000410000000000014005764144017761 5ustar www-datawww-datacelluloid-0.18.0/lib/celluloid/logging/incident_logger.rb0000644000004100000410000000731714005764144023452 0ustar www-datawww-datarequire "logger" module Celluloid # A logger that holds all messages in circular buffers, then flushes the buffers # when an event occurs at a configurable severity threshold. # # Unlike ruby's Logger, this class only supports a single progname. class IncidentLogger module Severity include ::Logger::Severity TRACE = -1 def severity_to_string(severity) case severity when TRACE then "TRACE" when DEBUG then "DEBUG" when INFO then "INFO" when WARN then "WARN" when ERROR then "ERROR" when FATAL then "FATAL" when UNKNOWN then "UNKNOWN" end end end include Severity # The progname (facility) for this instance. attr_accessor :progname # The logging level. Messages below this severity will not be logged at all. attr_accessor :level # The incident threshold. Messages at or above this severity will generate an # incident and be published to incident reporters. attr_accessor :threshold # The buffer size limit. Each log level will retain this number of messages # at maximum. attr_accessor :sizelimit attr_accessor :buffers # Create a new IncidentLogger. def initialize(progname = nil, options = {}) @progname = progname || "default" @level = options[:level] || DEBUG @threshold = options[:threshold] || ERROR @sizelimit = options[:sizelimit] || 100 @buffer_mutex = Mutex.new @buffers = Hash.new do |progname_hash, pn| @buffer_mutex.synchronize do progname_hash[pn] = Hash.new do |severity_hash, severity| severity_hash[severity] = RingBuffer.new(@sizelimit) end end end # When the IncidentLogger itself encounters an error, it falls back to logging to stderr @fallback_logger = ::Logger.new(STDERR) @fallback_logger.progname = "FALLBACK" end # add an event. def add(severity, message = nil, progname = nil, &block) progname ||= @progname severity ||= UNKNOWN return event.id if severity < @level if message.nil? && !block_given? message = progname progname = @progname end event = LogEvent.new(severity, message, progname, &block) @buffers[progname][severity] << event if severity >= @threshold begin Celluloid::Notifications.notifier.async.publish(incident_topic, create_incident(event)) rescue => ex @fallback_logger.error(ex) end end event.id end alias log add # See docs for Logger#info def trace(progname = nil, &block) add(TRACE, nil, progname, &block) end def debug(progname = nil, &block) add(DEBUG, nil, progname, &block) end def info(progname = nil, &block) add(INFO, nil, progname, &block) end def warn(progname = nil, &block) add(WARN, nil, progname, &block) end def error(progname = nil, &block) add(ERROR, nil, progname, &block) end def fatal(progname = nil, &block) add(FATAL, nil, progname, &block) end def unknown(progname = nil, &block) add(UNKNOWN, nil, progname, &block) end def flush messages = [] @buffer_mutex.synchronize do @buffers.each do |_progname, severities| severities.each do |_severity, buffer| messages += buffer.flush end end end messages.sort end def clear @buffer_mutex.synchronize do @buffers.each(&:clear) end end def create_incident(event = nil) Incident.new(flush, event) end def incident_topic "log.incident.#{@progname}" end end end celluloid-0.18.0/lib/celluloid/logging/incident_reporter.rb0000644000004100000410000000214114005764144024023 0ustar www-datawww-datarequire "logger" module Celluloid # Subscribes to log incident topics to report on them. class IncidentReporter include Celluloid include Celluloid::Notifications # get the time from the event class Formatter < ::Logger::Formatter def call(severity, _time, progname, msg) super(severity, msg.time, progname, msg.message) end end def initialize(*args) subscribe(/log\.incident/, :report) @logger = ::Logger.new(*args) @logger.formatter = Formatter.new @silenced = false end def report(_topic, incident) return if @silenced header = "INCIDENT" header << " AT #{incident.triggering_event.time}" if incident.triggering_event @logger << header @logger << "\n" @logger << "====================\n" incident.events.each do |event| @logger.add(event.severity, event, event.progname) end @logger << "====================\n" end def silence @silenced = true end def unsilence @silenced = false end def silenced? @silenced end end end celluloid-0.18.0/lib/celluloid/logging/log_event.rb0000644000004100000410000000111714005764144022270 0ustar www-datawww-datamodule Celluloid # Wraps a single log event. class LogEvent attr_accessor :id, :severity, :message, :progname, :time def initialize(severity, message, progname, time = Time.now, &_block) # This id should be ordered. For now relies on Celluloid::UUID to be ordered. # May want to use a generation/counter strategy for independence of uuid. @id = Internals::UUID.generate @severity = severity @message = block_given? ? yield : message @progname = progname @time = time end def <=>(other) @id <=> other.id end end end celluloid-0.18.0/lib/celluloid/logging/incident.rb0000644000004100000410000000120014005764144022074 0ustar www-datawww-datamodule Celluloid # Wraps all events and context for a single incident. class Incident attr_accessor :pid attr_accessor :events, :triggering_event def initialize(events, triggering_event = nil) @events = events @triggering_event = triggering_event @pid = $PROCESS_ID end # Merge two incidents together. This may be useful if two incidents occur at the same time. def merge(*other_incidents) merged_events = other_incidents.flatten.inject(events) do |events, incident| events += incident.events end Incident.new(merged_events.sort, triggering_event) end end end celluloid-0.18.0/lib/celluloid/logging/ring_buffer.rb0000644000004100000410000000204214005764144022574 0ustar www-datawww-datamodule Celluloid class RingBuffer def initialize(size) @size = size @start = 0 @count = 0 @buffer = Array.new(size) @mutex = Mutex.new end def full? @count == @size end def empty? @count == 0 end def push(value) @mutex.synchronize do stop = (@start + @count) % @size @buffer[stop] = value if full? @start = (@start + 1) % @size else @count += 1 end value end end alias << push def shift @mutex.synchronize do remove_element end end def flush values = [] @mutex.synchronize do values << remove_element until empty? end values end def clear @buffer = Array.new(@size) @start = 0 @count = 0 end private def remove_element return nil if empty? value = @buffer[@start] @buffer[@start] = nil @start = (@start + 1) % @size @count -= 1 value end end end celluloid-0.18.0/lib/celluloid/group/0000755000004100000410000000000014005764144017467 5ustar www-datawww-datacelluloid-0.18.0/lib/celluloid/group/spawner.rb0000644000004100000410000000301714005764144021474 0ustar www-datawww-datamodule Celluloid class Group class Spawner < Group attr_accessor :finalizer def initialize super end def get(&block) assert_active raise ArgumentError, "No block sent to Spawner.get()" unless block_given? instantiate block end def shutdown @running = false queue = [] @mutex.synchronize do loop do break if @group.empty? th = @group.shift th.kill queue << th end end Thread.pass unless queue.empty? loop do break if queue.empty? queue.pop.join end end def idle? to_a.select { |t| t[:celluloid_thread_state] == :running }.empty? end def busy? to_a.select { |t| t[:celluloid_thread_state] == :running }.any? end private def instantiate(proc) thread = Thread.new do Thread.current[:celluloid_thread_state] = :running begin proc.call rescue ::Exception => ex Internals::Logger.crash("thread crashed", ex) Thread.current[:celluloid_thread_state] = :error ensure Thread.current[:celluloid_thread_state] = :finished unless Thread.current[:celluloid_thread_state] == :error @mutex.synchronize { @group.delete Thread.current } Thread.exit end end @mutex.synchronize { @group << thread } thread end end end end celluloid-0.18.0/lib/celluloid/group/pool.rb0000644000004100000410000000557514005764144021001 0ustar www-datawww-datamodule Celluloid class Group class Pool < Group # You do not want to use this. Truly, you do not. There is no scenario when you will. # But. If you somehow do.. `Celluloid.group_class = Celluloid::Group::Pool` and weep. attr_accessor :max_idle def initialize super @mutex = Mutex.new @idle_threads = [] @group = [] @busy_size = 0 @idle_size = 0 # TODO: should really adjust this based on usage @max_idle = 16 end def idle? busy_size.count == 0 end def busy? busy_size.count > 0 end attr_reader :busy_size attr_reader :idle_size # Get a thread from the pool, running the given block def get(&block) @mutex.synchronize do assert_active begin if @idle_threads.empty? thread = create else thread = @idle_threads.pop @idle_size = @idle_threads.length end end until thread.status # handle crashed threads thread.busy = true @busy_size += 1 thread[:celluloid_queue] << block thread end end # Return a thread to the pool def put(thread) @mutex.synchronize do thread.busy = false if idle_size + 1 >= @max_idle thread[:celluloid_queue] << nil @busy_size -= 1 @group.delete(thread) else @idle_threads.push thread @busy_size -= 1 @idle_size = @idle_threads.length clean_thread_locals(thread) end end end def shutdown @running = false @mutex.synchronize do finalize @group.each do |thread| thread[:celluloid_queue] << nil end @group.shift.kill until @group.empty? @idle_threads.clear @busy_size = 0 @idle_size = 0 end end private # Create a new thread with an associated queue of procs to run def create queue = Queue.new thread = Thread.new do while proc = queue.pop begin proc.call rescue ::Exception => ex Internals::Logger.crash("thread crashed", ex) ensure put thread end end end thread[:celluloid_queue] = queue # @idle_threads << thread @group << thread thread end # Clean the thread locals of an incoming thread def clean_thread_locals(thread) thread.keys.each do |key| next if key == :celluloid_queue # Ruby seems to lack an API for deleting thread locals. WTF, Ruby? thread[key] = nil end end def finalize @max_idle = 0 end end end end celluloid-0.18.0/lib/celluloid/task/0000755000004100000410000000000014005764144017275 5ustar www-datawww-datacelluloid-0.18.0/lib/celluloid/task/fibered.rb0000644000004100000410000000313514005764144021224 0ustar www-datawww-datamodule Celluloid class Task # Tasks with a Fiber backend class Fibered < Task class StackError < Celluloid::Error; end def create queue = Thread.current[:celluloid_queue] actor_system = Thread.current[:celluloid_actor_system] @fiber = Fiber.new do # FIXME: cannot use the writer as specs run inside normal Threads Thread.current[:celluloid_role] = :actor Thread.current[:celluloid_queue] = queue Thread.current[:celluloid_actor_system] = actor_system yield # Alleged workaround for a MRI memory leak # TODO: validate/confirm this is actually necessary Fiber.yield if RUBY_ENGINE == "ruby" end end def signal Fiber.yield end # Resume a suspended task, giving it a value to return if needed def deliver(value) @fiber.resume value rescue SystemStackError => ex raise StackError, "#{ex} @#{meta[:method_name] || :unknown} (see https://github.com/celluloid/celluloid/wiki/Fiber-stack-errors)" rescue FiberError => ex raise DeadTaskError, "cannot resume a dead task (#{ex})" end # Terminate this task def terminate super rescue FiberError # If we're getting this the task should already be dead end def backtrace # rubocop:disable Metrics/LineLength ["#{self.class} backtrace unavailable. Please try `Celluloid.task_class = Celluloid::Task::Threaded` if you need backtraces here."] # rubocop:enable Metrics/LineLength end end end end celluloid-0.18.0/lib/celluloid/task/threaded.rb0000644000004100000410000000275314005764144021411 0ustar www-datawww-datamodule Celluloid class Task # Tasks with a Thread backend class Threaded < Task # Run the given block within a task def initialize(type, meta) @resume_queue = Queue.new @exception_queue = Queue.new @yield_mutex = Mutex.new @yield_cond = ConditionVariable.new @thread = nil super end def create # TODO: move this to ActorSystem#get_thread (ThreadHandle inside Group::Pool) thread = Internals::ThreadHandle.new(Thread.current[:celluloid_actor_system], :task) do begin ex = @resume_queue.pop raise ex if ex.is_a?(TaskTerminated) yield rescue ::Exception => ex @exception_queue << ex ensure @yield_mutex.synchronize do @yield_cond.signal end end end @thread = thread end def signal @yield_mutex.synchronize do @yield_cond.signal end @resume_queue.pop end def deliver(value) raise DeadTaskError, "cannot resume a dead task" unless @thread.alive? @yield_mutex.synchronize do @resume_queue.push(value) @yield_cond.wait(@yield_mutex) raise @exception_queue.pop until @exception_queue.empty? end rescue ThreadError raise DeadTaskError, "cannot resume a dead task" end def backtrace @thread.backtrace end end end end celluloid-0.18.0/lib/celluloid/group.rb0000644000004100000410000000225514005764144020020 0ustar www-datawww-datamodule Celluloid class Group attr_accessor :group def initialize @pid = $PROCESS_ID @mutex = Mutex.new @group = [] @running = true end def assert_active raise Celluloid::NotActive unless active? end def assert_inactive return unless active? if RUBY_PLATFORM == "java" Celluloid.logger.warn "Group is still active" else raise Celluloid::StillActive end end def each to_a.each { |thread| yield thread } end def forked? @pid != $PROCESS_ID end def to_a return [] if forked? res = nil @mutex.synchronize { res = @group.dup } res end def purge(thread) @mutex.synchronize do @group.delete(thread) begin thread.kill rescue nil end end end def each_actor(&block) to_a.lazy.select { |t| t[:celluloid_role] == :actor }.each(&block) end def active? @running end def get raise NotImplementedError end def create raise NotImplementedError end def shutdown raise NotImplementedError end end end celluloid-0.18.0/lib/celluloid/logging.rb0000644000004100000410000000031514005764144020305 0ustar www-datawww-datarequire "celluloid/logging/log_event" require "celluloid/logging/incident" require "celluloid/logging/ring_buffer" require "celluloid/logging/incident_logger" require "celluloid/logging/incident_reporter" celluloid-0.18.0/lib/celluloid/condition.rb0000644000004100000410000000456314005764144020656 0ustar www-datawww-datamodule Celluloid # ConditionVariable-like signaling between tasks and threads class Condition class Waiter def initialize(condition, task, mailbox, timeout) @condition = condition @task = task @mailbox = mailbox @timeout = timeout end attr_reader :condition, :task def <<(message) @mailbox << message end def wait begin message = @mailbox.receive(@timeout) do |msg| msg.is_a?(SignalConditionRequest) && msg.task == Thread.current end rescue TimedOut raise ConditionError, "timeout after #{@timeout.inspect} seconds" end until message message.value end end def initialize @mutex = Mutex.new @waiters = [] end # Wait for the given signal and return the associated value def wait(timeout = nil) raise ConditionError, "cannot wait for signals while exclusive" if Celluloid.exclusive? if actor = Thread.current[:celluloid_actor] task = Task.current if timeout bt = caller timer = actor.timers.after(timeout) do exception = ConditionError.new("timeout after #{timeout.inspect} seconds") exception.set_backtrace bt task.resume exception end end else task = Thread.current end waiter = Waiter.new(self, task, Celluloid.mailbox, timeout) @mutex.synchronize do @waiters << waiter end result = Celluloid.suspend :condwait, waiter timer.cancel if timer raise result if result.is_a?(ConditionError) return yield(result) if block_given? result end # Send a signal to the first task waiting on this condition def signal(value = nil) @mutex.synchronize do if waiter = @waiters.shift waiter << SignalConditionRequest.new(waiter.task, value) else Internals::Logger.with_backtrace(caller(3)) do |logger| logger.debug("Celluloid::Condition signaled spuriously") end end end end # Broadcast a value to all waiting tasks and threads def broadcast(value = nil) @mutex.synchronize do @waiters.each { |waiter| waiter << SignalConditionRequest.new(waiter.task, value) } @waiters.clear end end alias inspect to_s end end celluloid-0.18.0/lib/celluloid/actor/0000755000004100000410000000000014005764144017443 5ustar www-datawww-datacelluloid-0.18.0/lib/celluloid/actor/system.rb0000644000004100000410000000660414005764144021322 0ustar www-datawww-datamodule Celluloid extend Forwardable def_delegators :actor_system, :[], :[]= class Actor class System extend Forwardable def_delegators :@registry, :[], :get, :[]=, :set, :delete ROOT_SERVICES = [ { as: :notifications_fanout, type: Celluloid::Notifications::Fanout }, { as: :public_services, type: Celluloid::Supervision::Service::Public, accessors: [:services], supervise: [] } ].freeze attr_reader :registry, :group # the root of the supervisor tree is established at supervision/root def root_services @tree end def root_configuration @root end def initialize @tree = nil @group = Celluloid.group_class.new @registry = Internals::Registry.new @root = ROOT_SERVICES end # Launch default services def start within do @root = Supervision::Service::Root.define @tree = root_configuration.deploy end true end def within old = Thread.current[:celluloid_actor_system] Thread.current[:celluloid_actor_system] = self yield ensure Thread.current[:celluloid_actor_system] = old end def get_thread @group.get do Thread.current[:celluloid_actor_system] = self yield end end def stack_dump Internals::Stack::Dump.new(@group) end def stack_summary Internals::Stack::Summary.new(@group) end def registered @registry.names end def clear_registry @registry.clear end def running actors = [] @group.each do |t| next unless t.role == :actor actor = t.actor # NOTE - these are in separate statements, since on JRuby t.actor may # become nil befor .behavior_proxy() is called next unless actor next unless actor.respond_to?(:behavior_proxy) proxy = actor.behavior_proxy actors << proxy end actors end def running? @group.active? end # Shut down all running actors def shutdown actors = running Timeout.timeout(shutdown_timeout) do Internals::Logger.debug "Terminating #{actors.size} #{actors.size > 1 ? 'actors' : 'actor'}..." unless actors.empty? # Actors cannot self-terminate, you must do it for them actors.each do |actor| begin actor.terminate! rescue DeadActorError end end actors.each do |actor| begin Actor.join(actor) rescue DeadActorError end end end rescue Timeout::Error Internals::Logger.error("Couldn't cleanly terminate all actors in #{shutdown_timeout} seconds!") unless RUBY_PLATFORM == "java" || RUBY_ENGINE == "rbx" actors.each do |actor| begin Actor.kill(actor) rescue DeadActorError, MailboxDead end end end ensure @group.shutdown clear_registry end def assert_inactive @group.assert_inactive end def shutdown_timeout Celluloid.shutdown_timeout end end end end celluloid-0.18.0/lib/celluloid/exceptions.rb0000644000004100000410000000244714005764144021050 0ustar www-datawww-datamodule Celluloid class Error < StandardError; end class Interruption < RuntimeError; end class TimedOut < Celluloid::Interruption; end # Distinguished from `Timeout` class StillActive < Celluloid::Error; end class NotActive < Celluloid::Error; end class NotActorError < Celluloid::Error; end # Don't do Actor-like things outside Actor scope class DeadActorError < Celluloid::Error; end # Trying to do something to a dead actor class NotTaskError < Celluloid::Error; end # Asked to do task-related things outside a task class DeadTaskError < Celluloid::Error; end # Trying to resume a dead task class TaskTerminated < Celluloid::Interruption; end # Kill a running task after terminate class TaskTimeout < Celluloid::TimedOut; end # A timeout occurred before the given request could complete class ConditionError < Celluloid::Error; end class AbortError < Celluloid::Error # The sender made an error, not the current actor attr_reader :cause def initialize(cause) @cause = cause super "caused by #{cause.inspect}: #{cause}" end end class ThreadLeak < Celluloid::Error; end module Feature module Requires class RubiniusOrJRuby < Celluloid::Error; end class Rubinius < Celluloid::Error; end class JRuby < Celluloid::Error; end end end end celluloid-0.18.0/lib/celluloid/core_ext.rb0000644000004100000410000000013614005764144020470 0ustar www-datawww-dataclass Thread attr_accessor :uuid_counter, :uuid_limit def celluloid? false end end celluloid-0.18.0/lib/celluloid/mailbox/0000755000004100000410000000000014005764144017766 5ustar www-datawww-datacelluloid-0.18.0/lib/celluloid/mailbox/evented.rb0000644000004100000410000000407614005764144021754 0ustar www-datawww-datamodule Celluloid class Mailbox # An alternative implementation of Celluloid::Mailbox using Reactor class Evented < Celluloid::Mailbox attr_reader :reactor def initialize(reactor_class) super() # @condition won't be used in the class. @reactor = reactor_class.new end # Add a message to the Mailbox def <<(message) @mutex.lock begin if mailbox_full || @dead dead_letter(message) return end if message.is_a?(SystemEvent) # SystemEvents are high priority messages so they get added to the # head of our message queue instead of the end @messages.unshift message else @messages << message end ensure begin @mutex.unlock rescue nil end end begin current_actor = Thread.current[:celluloid_actor] @reactor.wakeup unless current_actor && current_actor.mailbox == self rescue => ex Internals::Logger.crash "reactor crashed", ex dead_letter(message) end nil end # Receive a message from the Mailbox def check(timeout = nil, &block) # Get a message if it is available and process it immediately if possible: if message = next_message(block) return message end # ... otherwise, run the reactor once, either blocking or will return # after the given timeout: @reactor.run_once(timeout) # No message was received: nil end # Obtain the next message from the mailbox that matches the given block def next_message(block) @mutex.lock begin super(&block) ensure begin @mutex.unlock rescue nil end end end # Cleanup any IO objects this Mailbox may be using def shutdown super do @reactor.shutdown end end end end end celluloid-0.18.0/lib/celluloid/mailbox.rb0000644000004100000410000000762714005764144020327 0ustar www-datawww-datamodule Celluloid class MailboxDead < Celluloid::Error; end # you can't receive from the dead class MailboxShutdown < Celluloid::Error; end # raised if the mailbox can no longer be used # Actors communicate with asynchronous messages. Messages are buffered in # Mailboxes until Actors can act upon them. class Mailbox include Enumerable # A unique address at which this mailbox can be found attr_reader :address attr_accessor :max_size def initialize @address = Celluloid.uuid @messages = [] @mutex = Mutex.new @dead = false @condition = ConditionVariable.new @max_size = nil end # Add a message to the Mailbox def <<(message) @mutex.lock begin if mailbox_full || @dead dead_letter(message) return end if message.is_a?(SystemEvent) # SystemEvents are high priority messages so they get added to the # head of our message queue instead of the end @messages.unshift message else @messages << message end @condition.signal nil ensure begin @mutex.unlock rescue nil end end end # Receive a message from the Mailbox. May return nil and may return before # the specified timeout. def check(timeout = nil, &block) message = nil @mutex.lock begin raise MailboxDead, "attempted to receive from a dead mailbox" if @dead message = nil Timers::Wait.for(timeout) do |remaining| message = next_message(&block) break if message @condition.wait(@mutex, remaining) end ensure begin @mutex.unlock rescue nil end end message end # Receive a letter from the mailbox. Guaranteed to return a message. If # timeout is exceeded, raise a TaskTimeout. def receive(timeout = nil, &block) message = nil Timers::Wait.for(timeout) do |_remaining| message = check(timeout, &block) break if message end return message if message raise TaskTimeout, "receive timeout exceeded" end # Shut down this mailbox and clean up its contents def shutdown raise MailboxDead, "mailbox already shutdown" if @dead @mutex.lock begin yield if block_given? messages = @messages @messages = [] @dead = true ensure begin @mutex.unlock rescue nil end end messages.each do |msg| dead_letter msg msg.cleanup if msg.respond_to? :cleanup end true end # Is the mailbox alive? def alive? !@dead end # Cast to an array def to_a @mutex.synchronize { @messages.dup } end # Iterate through the mailbox def each(&block) to_a.each(&block) end # Inspect the contents of the Mailbox def inspect "#<#{self.class}:#{object_id.to_s(16)} @messages=[#{map(&:inspect).join(', ')}]>" end # Number of messages in the Mailbox def size @mutex.synchronize { @messages.size } end private # Retrieve the next message in the mailbox def next_message message = nil if block_given? index = @messages.index do |msg| yield(msg) || msg.is_a?(SystemEvent) end message = @messages.slice!(index, 1).first if index else message = @messages.shift end message end def dead_letter(message) # !!! DO NOT INTRODUCE ADDITIONAL GLOBAL VARIABLES !!! # rubocop:disable Style/GlobalVars Internals::Logger.debug "Discarded message (mailbox is dead): #{message}" if $CELLULOID_DEBUG # rubocop:enable Style/GlobalVars end def mailbox_full @max_size && @messages.size >= @max_size end end end celluloid-0.18.0/lib/celluloid/test.rb0000644000004100000410000000026314005764144017640 0ustar www-datawww-data# !!! DO NOT INTRODUCE ADDITIONAL GLOBAL VARIABLES !!! # rubocop:disable Style/GlobalVars $CELLULOID_TEST = true # rubocop:enable Style/GlobalVars # require "celluloid/autostart" celluloid-0.18.0/lib/celluloid/debug.rb0000644000004100000410000000022414005764144017744 0ustar www-datawww-data# !!! DO NOT INTRODUCE ADDITIONAL GLOBAL VARIABLES !!! # rubocop:disable Style/GlobalVars $CELLULOID_DEBUG = true # rubocop:enable Style/GlobalVars celluloid-0.18.0/lib/celluloid/task.rb0000644000004100000410000001241714005764144017627 0ustar www-datawww-datamodule Celluloid # Tasks are interruptable/resumable execution contexts used to run methods class Task # Obtain the current task def self.current Thread.current[:celluloid_task] || raise(NotTaskError, "not within a task context") end # Suspend the running task, deferring to the scheduler def self.suspend(status) Task.current.suspend(status) end attr_reader :type, :meta, :status attr_accessor :chain_id, :guard_warnings # Create a new task def initialize(type, meta) @type = type @meta = meta @status = :new @exclusive = false @dangerous_suspend = @meta ? @meta.dup.delete(:dangerous_suspend) : false @guard_warnings = false actor = Thread.current[:celluloid_actor] @chain_id = Internals::CallChain.current_id raise NotActorError, "can't create tasks outside of actors" unless actor guard "can't create tasks inside of tasks" if Thread.current[:celluloid_task] create do begin @status = :running actor.setup_thread name_current_thread thread_metadata Thread.current[:celluloid_task] = self Internals::CallChain.current_id = @chain_id actor.tasks << self yield rescue TaskTerminated # Task was explicitly terminated ensure name_current_thread nil @status = :dead actor.tasks.delete self end end end def create(&_block) raise "Implement #{self.class}#create" end # Suspend the current task, changing the status to the given argument def suspend(status) raise "Cannot suspend while in exclusive mode" if exclusive? raise "Cannot suspend a task from outside of itself" unless Task.current == self @status = status # !!! DO NOT INTRODUCE ADDITIONAL GLOBAL VARIABLES !!! # rubocop:disable Style/GlobalVars if $CELLULOID_DEBUG && @dangerous_suspend Internals::Logger.with_backtrace(caller[2...8]) do |logger| logger.warn "Dangerously suspending task: type=#{@type.inspect}, meta=#{@meta.inspect}, status=#{@status.inspect}" end end # rubocop:enable Style/GlobalVars value = signal @status = :running raise value if value.is_a?(Celluloid::Interruption) value end # Resume a suspended task, giving it a value to return if needed def resume(value = nil) guard "Cannot resume a task from inside of a task" if Thread.current[:celluloid_task] if running? deliver(value) else # rubocop:disable Metrics/LineLength Internals::Logger.warn "Attempted to resume a dead task: type=#{@type.inspect}, meta=#{@meta.inspect}, status=#{@status.inspect}" # rubocop:enable Metrics/LineLength end nil end # Execute a code block in exclusive mode. def exclusive if @exclusive yield else begin @exclusive = true yield ensure @exclusive = false end end end # Terminate this task def terminate raise "Cannot terminate an exclusive task" if exclusive? if running? # !!! DO NOT INTRODUCE ADDITIONAL GLOBAL VARIABLES !!! # rubocop:disable Style/GlobalVars if $CELLULOID_DEBUG Internals::Logger.with_backtrace(backtrace) do |logger| type = @dangerous_suspend ? :warn : :debug logger.send(type, "Terminating task: type=#{@type.inspect}, meta=#{@meta.inspect}, status=#{@status.inspect}") end end # rubocop:enable Style/GlobalVars exception = TaskTerminated.new("task was terminated") exception.set_backtrace(caller) resume exception else raise DeadTaskError, "task is already dead" end end # Is this task running in exclusive mode? def exclusive? @exclusive end def backtrace; end # Is the current task still running? def running? @status != :dead end # Nicer string inspect for tasks def inspect "#<#{self.class}:0x#{object_id.to_s(16)} @type=#{@type.inspect}, @meta=#{@meta.inspect}, @status=#{@status.inspect}>" end def guard(message) # !!! DO NOT INTRODUCE ADDITIONAL GLOBAL VARIABLES !!! # rubocop:disable Style/GlobalVars if @guard_warnings Internals::Logger.warn message if $CELLULOID_DEBUG else raise message if $CELLULOID_DEBUG end # rubocop:enable Style/GlobalVars end private def name_current_thread(new_name) return unless RUBY_PLATFORM == "java" if new_name.nil? new_name = Thread.current[:celluloid_original_thread_name] Thread.current[:celluloid_original_thread_name] = nil else Thread.current[:celluloid_original_thread_name] = Thread.current.to_java.getNativeThread.get_name end Thread.current.to_java.getNativeThread.set_name(new_name) end def thread_metadata method = @meta && @meta[:method_name] || "" klass = Thread.current[:celluloid_actor] && Thread.current[:celluloid_actor].behavior.subject.bare_object.class || "" format("[Celluloid] %s#%s", klass, method) end end end celluloid-0.18.0/lib/celluloid/future.rb0000644000004100000410000000646714005764144020207 0ustar www-datawww-datamodule Celluloid # Celluloid::Future objects allow methods and blocks to run in the # background, their values requested later class Future def self.new(*args, &block) return super unless block future = new # task = Thread.current[:celluloid_task] # actor = Thread.current[:celluloid_actor] Internals::ThreadHandle.new(Celluloid.actor_system, :future) do begin # Thread.current[:celluloid_task] = task # Thread.current[:celluloid_actor] = actor call = Call::Sync.new(future, :call, args) call.dispatch(block) rescue # Exceptions in blocks will get raised when the value is retrieved end end future end attr_reader :address def initialize(&block) @address = Celluloid.uuid @mutex = Mutex.new @ready = false @result = nil @forwards = nil @cancelled = false if block @call = Call::Sync.new(self, :call, args) Celluloid.internal_pool.get do begin @call.dispatch(block) rescue # Exceptions in blocks will get raised when the value is retrieved end end else @call = nil end end # Execute the given method in future context def execute(receiver, method, args, block) @mutex.synchronize do raise "already calling" if @call @call = Call::Sync.new(self, method, args, block) end receiver << @call end # Check if this future has a value yet def ready? @ready end # Obtain the value for this Future def value(timeout = nil) ready = result = nil begin @mutex.lock if @ready ready = true result = @result else case @forwards when Array @forwards << Celluloid.mailbox when NilClass @forwards = Celluloid.mailbox else @forwards = [@forwards, Celluloid.mailbox] end end ensure @mutex.unlock end unless ready result = Celluloid.receive(timeout) do |msg| msg.is_a?(Future::Result) && msg.future == self end end if result result.respond_to?(:value) ? result.value : result else raise TimedOut, "Timed out" end end alias call value # Signal this future with the given result value def signal(value) return if @cancelled result = Result.new(value, self) @mutex.synchronize do raise "the future has already happened!" if @ready if @forwards @forwards.is_a?(Array) ? @forwards.each { |f| f << result } : @forwards << result end @result = result @ready = true end end alias << signal def cancel(error) response = Internals::Response::Error.new(@call, error) signal response @mutex.synchronize do @cancelled = true end end # Inspect this Celluloid::Future alias inspect to_s # Wrapper for result values to distinguish them in mailboxes class Result attr_reader :future def initialize(result, future) @result = result @future = future end def value @result.value end end end end celluloid-0.18.0/lib/celluloid/supervision.rb0000644000004100000410000000107414005764144021250 0ustar www-datawww-datarequire "celluloid" unless defined? Celluloid require "celluloid/supervision/constants" require "celluloid/supervision/supervise" require "celluloid/supervision/container" require "celluloid/supervision/container/instance" require "celluloid/supervision/container/behavior" require "celluloid/supervision/container/injections" require "celluloid/supervision/container/behavior/tree" require "celluloid/supervision/validation" require "celluloid/supervision/configuration" require "celluloid/supervision/configuration/instance" require "celluloid/supervision/service" celluloid-0.18.0/lib/celluloid/thread.rb0000644000004100000410000000140414005764144020126 0ustar www-datawww-datamodule Celluloid class Thread < ::Thread def celluloid? true end attr_accessor :busy # Obtain the role of this thread def role self[:celluloid_role] end def role=(role) self[:celluloid_role] = role end # Obtain the Celluloid::Actor object for this thread def actor self[:celluloid_actor] end # Obtain the Celluloid task object for this thread def task self[:celluloid_task] end # Obtain the Celluloid mailbox for this thread def mailbox self[:celluloid_mailbox] end # Obtain the call chain ID for this thread def call_chain_id self[:celluloid_chain_id] end def <<(proc) self[:celluloid_queue] << proc self end end end celluloid-0.18.0/lib/celluloid/proxy/0000755000004100000410000000000014005764144017514 5ustar www-datawww-datacelluloid-0.18.0/lib/celluloid/proxy/cell.rb0000644000004100000410000000305014005764144020756 0ustar www-datawww-data# A proxy object returned from Celluloid::Actor.new/new_link which converts # the normal Ruby method protocol into an inter-actor message protocol class Celluloid::Proxy::Cell < Celluloid::Proxy::Sync def initialize(mailbox, actor_proxy, klass) super(mailbox, klass) @actor_proxy = actor_proxy @async_proxy = ::Celluloid::Proxy::Async.new(mailbox, klass) @future_proxy = ::Celluloid::Proxy::Future.new(mailbox, klass) end def _send_(meth, *args, &block) method_missing :__send__, meth, *args, &block end def inspect method_missing :inspect rescue ::Celluloid::DeadActorError "#<::Celluloid::Proxy::Cell(#{@klass}) dead>" end def method(name) ::Celluloid::Internals::Method.new(self, name) end alias sync method_missing # Obtain an async proxy or explicitly invoke a named async method def async(method_name = nil, *args, &block) if method_name @async_proxy.method_missing method_name, *args, &block else @async_proxy end end # Obtain a future proxy or explicitly invoke a named future method def future(method_name = nil, *args, &block) if method_name @future_proxy.method_missing method_name, *args, &block else @future_proxy end end def alive? @actor_proxy.alive? end def dead? @actor_proxy.dead? end def thread @actor_proxy.thread end # Terminate the associated actor def terminate @actor_proxy.terminate end # Terminate the associated actor asynchronously def terminate! @actor_proxy.terminate! end end celluloid-0.18.0/lib/celluloid/proxy/abstract.rb0000644000004100000410000000233014005764144021642 0ustar www-datawww-datamodule Celluloid::Proxy # Looks up the actual class of instance, even if instance is a proxy. def self.class_of(instance) (class << instance; self; end).superclass end end # Base class of Celluloid proxies class Celluloid::Proxy::Abstract < BasicObject # Needed for storing proxies in data structures needed = %i[object_id __id__ hash eql? private_methods] - instance_methods if needed.any? include ::Kernel.dup.module_eval { undef_method(*(instance_methods - needed)) self } # rubinius bug? These methods disappear when we include hacked kernel define_method :==, ::BasicObject.instance_method(:==) unless instance_methods.include?(:==) alias equal? == unless instance_methods.include?(:equal?) end def __class__ @class ||= ::Celluloid::Proxy.class_of(self) end end class Celluloid::Proxy::AbstractCall < Celluloid::Proxy::Abstract attr_reader :mailbox def initialize(mailbox, klass) @mailbox = mailbox @klass = klass end def eql?(other) __class__.eql?(::Celluloid::Proxy.class_of(other)) && @mailbox.eql?(other.mailbox) end def hash @mailbox.hash end def __klass__ @klass end def inspect "#<#{__class__}(#{@klass})>" end end celluloid-0.18.0/lib/celluloid/proxy/actor.rb0000644000004100000410000000152214005764144021151 0ustar www-datawww-data# A proxy which controls the Actor lifecycle class Celluloid::Proxy::Actor < Celluloid::Proxy::Abstract attr_reader :thread, :mailbox def initialize(mailbox, thread) @mailbox = mailbox @thread = thread end def inspect # TODO: use a system event to fetch actor state: tasks? "#" rescue DeadActorError "#" end def alive? @mailbox.alive? end def dead? !alive? end # Terminate the associated actor def terminate terminate! ::Celluloid::Actor.join(self) nil end # Terminate the associated actor asynchronously def terminate! ::Kernel.raise ::Celluloid::DeadActorError, "actor already terminated" unless alive? @mailbox << ::Celluloid::TerminationRequest.new end end celluloid-0.18.0/lib/celluloid/proxy/async.rb0000644000004100000410000000071214005764144021156 0ustar www-datawww-data# A proxy which sends asynchronous calls to an actor class Celluloid::Proxy::Async < Celluloid::Proxy::AbstractCall def method_missing(meth, *args, &block) if @mailbox == ::Thread.current[:celluloid_mailbox] args.unshift meth meth = :__send__ end if block_given? # FIXME: nicer exception raise "Cannot use blocks with async yet" end @mailbox << ::Celluloid::Call::Async.new(meth, args, block) self end end celluloid-0.18.0/lib/celluloid/proxy/future.rb0000644000004100000410000000100414005764144021346 0ustar www-datawww-data# A proxy which creates future calls to an actor class Celluloid::Proxy::Future < Celluloid::Proxy::AbstractCall def method_missing(meth, *args, &block) raise ::Celluloid::DeadActorError, "attempted to call a dead actor: #{meth}" unless @mailbox.alive? if block_given? # FIXME: nicer exception raise "Cannot use blocks with futures yet" end future = ::Celluloid::Future.new call = ::Celluloid::Call::Sync.new(future, meth, args, block) @mailbox << call future end end celluloid-0.18.0/lib/celluloid/proxy/block.rb0000644000004100000410000000125614005764144021137 0ustar www-datawww-dataclass Celluloid::Proxy::Block attr_writer :execution attr_reader :call, :block def initialize(mailbox, call, block) @mailbox = mailbox @call = call @block = block @execution = :sender end def to_proc if @execution == :sender lambda do |*values| if task = Thread.current[:celluloid_task] @mailbox << ::Celluloid::Call::Block.new(self, ::Celluloid::Actor.current.mailbox, values) # TODO: if respond fails, the Task will never be resumed task.suspend(:invokeblock) else # FIXME: better exception raise "No task to suspend" end end else @block end end end celluloid-0.18.0/lib/celluloid/proxy/sync.rb0000644000004100000410000000144014005764144021014 0ustar www-datawww-data# A proxy which sends synchronous calls to an actor class Celluloid::Proxy::Sync < Celluloid::Proxy::AbstractCall def respond_to?(meth, include_private = false) __class__.instance_methods.include?(meth) || method_missing(:respond_to?, meth, include_private) end def method_missing(meth, *args, &block) raise ::Celluloid::DeadActorError, "attempted to call a dead actor: #{meth}" unless @mailbox.alive? if @mailbox == ::Thread.current[:celluloid_mailbox] args.unshift meth meth = :__send__ # actor = Thread.current[:celluloid_actor] # actor = actor.behavior.subject.bare_object # return actor.__send__(*args, &block) end call = ::Celluloid::Call::Sync.new(::Celluloid.mailbox, meth, args, block) @mailbox << call call.value end end celluloid-0.18.0/lib/celluloid/system_events.rb0000644000004100000410000000607214005764144021575 0ustar www-datawww-datamodule Celluloid class Actor # Handle high-priority system event messages def handle_system_event(event) if handler = SystemEvent.handle(event.class) send(handler, event) else # !!! DO NOT INTRODUCE ADDITIONAL GLOBAL VARIABLES !!! # rubocop:disable Style/GlobalVars Internals::Logger.debug "Discarded message (unhandled): #{message}" if $CELLULOID_DEBUG # rubocop:enable Style/GlobalVars end end end # High-priority internal system events class SystemEvent class << self @@system_events = {} def handle(type) @@system_events[type] end def handler(&block) raise ArgumentError, "SystemEvent handlers must be defined with a block." unless block method = begin handler = name .split("::").last .gsub(/([A-Z]+)([A-Z][a-z])/, "\1_\2") .gsub(/([a-z\d])([A-Z])/, "\1_\2") .tr("-", "_") .downcase :"handle_#{handler}" end Actor.send(:define_method, method, &block) @@system_events[self] = method end end class LinkingEvent < SystemEvent # Shared initializer for LinkingRequest and LinkingResponse def initialize(actor, type) @actor = actor @type = type.to_sym raise ArgumentError, "type must be link or unlink" unless %i[link unlink].include?(@type) end end end # Request to link with another actor class LinkingRequest < SystemEvent::LinkingEvent attr_reader :actor, :type handler do |event| event.process(links) end def process(links) case type when :link then links << actor when :unlink then links.delete actor end actor.mailbox << LinkingResponse.new(Actor.current, type) end end # Response to a link request class LinkingResponse < SystemEvent::LinkingEvent attr_reader :actor, :type end # An actor has exited for the given reason class ExitEvent < SystemEvent attr_reader :actor, :reason handler do |event| @links.delete event.actor @exit_handler.call(event) end def initialize(actor, reason = nil) @actor = actor @reason = reason end end # Name an actor at the time it's registered class NamingRequest < SystemEvent attr_reader :name handler do |event| @name = event.name # !!! DO NOT INTRODUCE ADDITIONAL GLOBAL VARIABLES !!! # rubocop:disable Style/GlobalVars Celluloid::Probe.actor_named(self) if $CELLULOID_MONITORING # rubocop:enable Style/GlobalVars end def initialize(name) @name = name end end # Request for an actor to terminate class TerminationRequest < SystemEvent handler do |_event| terminate end end # Signal a condition class SignalConditionRequest < SystemEvent def initialize(task, value) @task = task @value = value end attr_reader :task, :value handler(&:call) def call @task.resume(@value) end end end celluloid-0.18.0/lib/celluloid/notifications.rb0000644000004100000410000000445014005764144021534 0ustar www-datawww-datamodule Celluloid module Notifications def self.notifier Actor[:notifications_fanout] || raise(DeadActorError, "notifications fanout actor not running") end def publish(pattern, *args) Celluloid::Notifications.notifier.publish(pattern, *args) rescue DeadActorError # Bad shutdown logic. Oh well.... # TODO: needs a tests end module_function :publish def subscribe(pattern, method) Celluloid::Notifications.notifier.subscribe(Actor.current, pattern, method) end def unsubscribe(*args) Celluloid::Notifications.notifier.unsubscribe(*args) end class Fanout include Celluloid trap_exit :prune def initialize @subscribers = [] @listeners_for = {} end def subscribe(actor, pattern, method) subscriber = Subscriber.new(actor, pattern, method).tap do |s| @subscribers << s end link actor @listeners_for.clear subscriber end def unsubscribe(subscriber) @subscribers.reject! { |s| s.matches?(subscriber) } @listeners_for.clear end def publish(pattern, *args) listeners_for(pattern).each { |s| s.publish(pattern, *args) } end def listeners_for(pattern) @listeners_for[pattern] ||= @subscribers.select { |s| s.subscribed_to?(pattern) } end def listening?(pattern) listeners_for(pattern).any? end def prune(actor, _reason = nil) @subscribers.reject! { |s| s.actor == actor } @listeners_for.clear end end class Subscriber attr_accessor :actor, :pattern, :method def initialize(actor, pattern, method) @actor = actor @pattern = pattern @method = method end def publish(pattern, *args) actor.async method, pattern, *args rescue DeadActorError # TODO: needs a tests # Bad shutdown logic. Oh well.... end def subscribed_to?(pattern) !pattern || @pattern === pattern.to_s || @pattern === pattern end def matches?(subscriber_or_pattern) self === subscriber_or_pattern || @pattern && @pattern === subscriber_or_pattern end end end def self.publish(*args) Notifications.publish(*args) end end celluloid-0.18.0/lib/celluloid/proxies.rb0000644000004100000410000000045014005764144020350 0ustar www-datawww-datamodule Celluloid module Proxy require "celluloid/proxy/abstract" require "celluloid/proxy/sync" require "celluloid/proxy/cell" require "celluloid/proxy/actor" require "celluloid/proxy/async" require "celluloid/proxy/future" require "celluloid/proxy/block" end end celluloid-0.18.0/lib/celluloid/calls.rb0000644000004100000410000000335414005764144017763 0ustar www-datawww-datamodule Celluloid # Calls represent requests to an actor class Call attr_reader :method, :arguments, :block def initialize(method, arguments = [], block = nil) @retry = 0 @method = method @arguments = arguments if block if Celluloid.exclusive? # FIXME: nicer exception raise "Cannot execute blocks on sender in exclusive mode" end @block = Proxy::Block.new(Celluloid.mailbox, self, block) else @block = nil end end def execute_block_on_receiver @block && @block.execution = :receiver end def dispatch(obj) check(obj) obj.public_send(@method, *@arguments, &(@block && @block.to_proc)) end def check(obj) # NOTE: don't use respond_to? here begin meth = obj.method(@method) rescue NameError raise NoMethodError, "undefined method `#{@method}' for #<#{obj.class}:0x#{obj.object_id.to_s(16)}>" end arity = meth.arity if arity >= 0 if @arguments.size != arity e = ArgumentError.new("wrong number of arguments (#{@arguments.size} for #{arity})") e.set_backtrace(caller << "#{meth.source_location.join(':')}: in `#{meth.name}`") raise e end elsif arity < -1 mandatory_args = -arity - 1 if arguments.size < mandatory_args e = ArgumentError.new("wrong number of arguments (#{@arguments.size} for #{mandatory_args}+)") e.set_backtrace(caller << "#{meth.source_location.join(':')}: in `#{meth.name}`") raise e end end rescue => ex raise AbortError, ex end end end require "celluloid/call/sync" require "celluloid/call/async" require "celluloid/call/block" celluloid-0.18.0/lib/celluloid/internals/0000755000004100000410000000000014005764144020332 5ustar www-datawww-datacelluloid-0.18.0/lib/celluloid/internals/thread_handle.rb0000644000004100000410000000267714005764144023455 0ustar www-datawww-datamodule Celluloid module Internals # An abstraction around threads from the InternalPool which ensures we don't # accidentally do things to threads which have been returned to the pool, # such as, say, killing them class ThreadHandle def initialize(actor_system, role = nil) @mutex = Mutex.new @join = ConditionVariable.new @thread = actor_system.get_thread do Thread.current.role = role begin yield ensure @mutex.synchronize do @thread = nil @join.broadcast end end end end # Is the thread running? def alive? @mutex.synchronize { @thread && @thread.alive? } end # Forcibly kill the thread def kill @mutex.synchronize { @thread && @thread.kill } self end # Join to a running thread, blocking until it terminates def join(limit = nil) raise ThreadError, "Target thread must not be current thread" if @thread == Thread.current @mutex.synchronize { @join.wait(@mutex, limit) if @thread } self end # Obtain the backtrace for this thread def backtrace @thread.backtrace rescue NoMethodError # undefined method `backtrace' for nil:NilClass # Swallow this in case this ThreadHandle was terminated and @thread was # set to nil end end end end celluloid-0.18.0/lib/celluloid/internals/method.rb0000644000004100000410000000136314005764144022142 0ustar www-datawww-datamodule Celluloid module Internals # Method handles that route through an actor proxy class Method def initialize(proxy, name) raise NoMethodError, "undefined method `#{name}'" unless proxy.respond_to? name @proxy = proxy @name = name @klass = @proxy.class end def arity @proxy.method_missing(:method, @name).arity end def name @proxy.method_missing(:method, @name).name end def parameters @proxy.method_missing(:method, @name).parameters end def call(*args, &block) @proxy.__send__(@name, *args, &block) end def inspect "#" end end end end celluloid-0.18.0/lib/celluloid/internals/stack/0000755000004100000410000000000014005764144021437 5ustar www-datawww-datacelluloid-0.18.0/lib/celluloid/internals/stack/dump.rb0000644000004100000410000000030714005764144022731 0ustar www-datawww-datamodule Celluloid module Internals class Stack class Dump < Stack def initialize(threads) super(threads) snapshot(true) end end end end end celluloid-0.18.0/lib/celluloid/internals/stack/summary.rb0000644000004100000410000000030414005764144023456 0ustar www-datawww-datamodule Celluloid module Internals class Stack class Summary < Stack def initialize(threads) super(threads) snapshot end end end end end celluloid-0.18.0/lib/celluloid/internals/stack/states.rb0000644000004100000410000000411214005764144023265 0ustar www-datawww-datamodule Celluloid module Internals class Stack module DisplayBacktrace def display_backtrace(backtrace, output, indent = nil) backtrace ||= ["EMPTY BACKTRACE"] backtrace.each do |line| output << indent if indent output << "\t" << line << "\n" end output << "\n\n" end end class TaskState < Struct.new(:task_class, :type, :meta, :status, :backtrace); end class CellState < Struct.new(:subject_id, :subject_class) def dump "Celluloid::Cell 0x#{subject_id.to_s(16)}: #{subject_class}" end end class ThreadState < Struct.new(:thread_id, :backtrace, :role) include DisplayBacktrace def dump string = "" string << "Thread 0x#{thread_id.to_s(16)} (#{role}):\n" display_backtrace backtrace, string if backtrace string end end class ActorState include DisplayBacktrace attr_accessor :name, :id, :cell attr_accessor :status, :tasks attr_accessor :backtrace def dump string = "" string << "Celluloid::Actor 0x#{id.to_s(16)}" string << " [#{name}]" if name string << "\n" if cell string << cell.dump string << "\n" end if status == :idle string << "State: Idle (waiting for messages)\n" display_backtrace backtrace, string if backtrace else string << "State: Running (executing tasks)\n" display_backtrace backtrace, string if backtrace string << "\tTasks:\n" tasks.each_with_index do |task, i| string << "\t #{i + 1}) #{task.task_class}[#{task.type}]: #{task.status}\n" if task.backtrace string << "\t #{task.meta.inspect}\n" display_backtrace task.backtrace, string, "\t" end end end string << "\n" unless backtrace string end end end end end celluloid-0.18.0/lib/celluloid/internals/properties.rb0000644000004100000410000000174114005764144023056 0ustar www-datawww-datamodule Celluloid module Internals # Properties define inheritable attributes of classes, somewhat similar to # Rails cattr_*/mattr_* or class_attribute module Properties def property(name, opts = {}) default = opts.fetch(:default, nil) multi = opts.fetch(:multi, false) ivar_name = "@#{name}".to_sym singleton = class << ancestors.first; self; end begin singleton.send(:remove_method, name) rescue nil end singleton.send(:define_method, name) do |value = nil, *extra| if value value = value ? [value, *send(name), *extra].uniq : [] if multi instance_variable_set(ivar_name, value) elsif instance_variables.include?(ivar_name) instance_variable_get(ivar_name) elsif superclass.respond_to? name superclass.send(name) else default end end end end end end celluloid-0.18.0/lib/celluloid/internals/signals.rb0000644000004100000410000000121314005764144022314 0ustar www-datawww-datamodule Celluloid module Internals # Event signaling between methods of the same object class Signals def initialize @conditions = {} end # Wait for the given signal and return the associated value def wait(name) raise "cannot wait for signals while exclusive" if Celluloid.exclusive? @conditions[name] ||= Condition.new @conditions[name].wait end # Send a signal to all method calls waiting for the given name def broadcast(name, value = nil) condition = @conditions.delete(name) condition.broadcast(value) if condition end end end end celluloid-0.18.0/lib/celluloid/internals/cpu_counter.rb0000644000004100000410000000303514005764144023206 0ustar www-datawww-datamodule Celluloid module Internals module CPUCounter class << self def cores @cores ||= count_cores end def count_cores from_result(from_env || from_sysdev || from_java || from_proc || from_win32ole || from_sysctl) || 1 end def from_env result = ENV["NUMBER_OF_PROCESSORS"] result if result && !result.empty? rescue end def from_sysdev ::IO.read("/sys/devices/system/cpu/present").split("-").last.to_i + 1 rescue Errno::ENOENT begin result = Dir["/sys/devices/system/cpu/cpu*"].count { |n| n =~ /cpu\d+/ } result unless result.zero? rescue end rescue end def from_java Java::Java.lang.Runtime.getRuntime.availableProcessors if defined? Java::Java rescue end def from_proc File.read("/proc/cpuinfo").scan(/^processor\s*:/).size if File.exist?("/proc/cpuinfo") rescue end def from_win32ole require "win32ole" WIN32OLE.connect("winmgmts://").ExecQuery("select * from Win32_ComputerSystem").NumberOfProcessors rescue LoadError rescue end def from_sysctl Integer `sysctl -n hw.ncpu 2>/dev/null` rescue end def from_result(result) if result i = Integer(result.to_s[/\d+/], 10) return i if i > 0 end rescue end end end end end celluloid-0.18.0/lib/celluloid/internals/call_chain.rb0000644000004100000410000000052014005764144022731 0ustar www-datawww-datamodule Celluloid module Internals class CallChain def self.current_id=(value) Thread.current[:celluloid_chain_id] = value task = Thread.current[:celluloid_task] task.chain_id = value if task end def self.current_id Thread.current[:celluloid_chain_id] end end end end celluloid-0.18.0/lib/celluloid/internals/task_set.rb0000644000004100000410000000216514005764144022500 0ustar www-datawww-datarequire "set" require "forwardable" module Celluloid module Internals if RUBY_PLATFORM == "java" require "jruby/synchronized" class TaskSet extend Forwardable include JRuby::Synchronized def_delegators :@tasks, :<<, :delete, :first, :empty?, :to_a def initialize @tasks = Set.new end end elsif RUBY_ENGINE == "rbx" class TaskSet def initialize @tasks = Set.new end def <<(task) Rubinius.synchronize(self) { @tasks << task } end def delete(task) Rubinius.synchronize(self) { @tasks.delete task } end def first Rubinius.synchronize(self) { @tasks.first } end def empty? Rubinius.synchronize(self) { @tasks.empty? } end def to_a Rubinius.synchronize(self) { @tasks.to_a } end end else # Assume we're on MRI, where we have the GIL. But what about IronRuby? # Or MacRuby. Do people care? This will break Celluloid::Internals::StackDumps TaskSet = Set end end end celluloid-0.18.0/lib/celluloid/internals/handlers.rb0000644000004100000410000000153314005764144022461 0ustar www-datawww-datarequire "set" module Celluloid module Internals class Handlers def initialize @handlers = Set.new end def handle(*patterns, &block) patterns.each do |pattern| handler = Handler.new pattern, block @handlers << handler end end # Handle incoming messages def handle_message(message) handler = @handlers.find { |h| h.match(message) } handler.call(message) if handler handler end end # Methods blocking on a call to receive class Handler def initialize(pattern, block) @pattern = pattern @block = block end # Match a message with this receiver's block def match(message) @pattern === message end def call(message) @block.call message end end end end celluloid-0.18.0/lib/celluloid/internals/uuid.rb0000644000004100000410000000224614005764144021631 0ustar www-datawww-datarequire "securerandom" module Celluloid module Internals # Clearly Ruby doesn't have enough UUID libraries # This one aims to be fast and simple with good support for multiple threads # If there's a better UUID library I can use with similar multithreaded # performance, I certainly wouldn't mind using a gem for this! module UUID values = SecureRandom.hex(9).match(/(.{8})(.{4})(.{3})(.{3})/) PREFIX = "#{values[1]}-#{values[2]}-4#{values[3]}-8#{values[4]}".freeze BLOCK_SIZE = 0x10000 @counter = 0 @counter_mutex = Mutex.new def self.generate thread = Thread.current unless thread.uuid_limit @counter_mutex.synchronize do block_base = @counter @counter += BLOCK_SIZE thread.uuid_counter = block_base thread.uuid_limit = @counter - 1 end end counter = thread.uuid_counter if thread.uuid_counter >= thread.uuid_limit thread.uuid_counter = thread.uuid_limit = nil else thread.uuid_counter += 1 end "#{PREFIX}-#{format('%012x', counter)}".freeze end end end end celluloid-0.18.0/lib/celluloid/internals/responses.rb0000644000004100000410000000162614005764144022705 0ustar www-datawww-datamodule Celluloid module Internals # Responses to calls class Response attr_reader :call, :value def initialize(call, value) @call = call @value = value end def dispatch @call.task.resume self end # Call completed successfully class Success < Response; end # Call was aborted due to sender error class Error < Response def value ex = super ex = ex.cause if ex.is_a? Celluloid::AbortError if ex.backtrace ex.backtrace << "(celluloid):0:in `remote procedure call'" ex.backtrace.concat(caller) end raise ex end end class Block def initialize(call, result) @call = call @result = result end def dispatch @call.task.resume(@result) end end end end end celluloid-0.18.0/lib/celluloid/internals/receivers.rb0000644000004100000410000000267514005764144022660 0ustar www-datawww-datarequire "set" require "timers" module Celluloid module Internals # Allow methods to directly interact with the actor protocol class Receivers def initialize(timers) @receivers = Set.new @timers = timers end # Receive an asynchronous message def receive(timeout = nil, &block) if Celluloid.exclusive? Celluloid.mailbox.receive(timeout, &block) else receiver = Receiver.new block if timeout receiver.timer = @timers.after(timeout) do @receivers.delete receiver receiver.resume end end @receivers << receiver Task.suspend :receiving end end # Handle incoming messages def handle_message(message) receiver = @receivers.find { |r| r.match(message) } return unless receiver @receivers.delete receiver receiver.timer.cancel if receiver.timer receiver.resume message message end end # Methods blocking on a call to receive class Receiver attr_accessor :timer def initialize(block) @block = block @task = Task.current @timer = nil end # Match a message with this receiver's block def match(message) @block ? @block.call(message) : true end def resume(message = nil) @task.resume message end end end end celluloid-0.18.0/lib/celluloid/internals/links.rb0000644000004100000410000000145314005764144022002 0ustar www-datawww-datamodule Celluloid module Internals # Linked actors send each other system events class Links include Enumerable def initialize @links = {} end # Add an actor to the current links def <<(actor) @links[actor.mailbox.address] = actor end # Do links include the given actor? def include?(actor) @links.key? actor.mailbox.address end # Remove an actor from the links def delete(actor) @links.delete actor.mailbox.address end # Iterate through all links def each @links.each { |_, actor| yield(actor) } end # Generate a string representation def inspect links = map(&:inspect).join(",") "#<#{self.class}[#{links}]>" end end end end celluloid-0.18.0/lib/celluloid/internals/logger.rb0000644000004100000410000000522414005764144022141 0ustar www-datawww-datamodule Celluloid module Internals module Logger class WithBacktrace def initialize(backtrace) @backtrace = backtrace end def debug(string) # !!! DO NOT INTRODUCE ADDITIONAL GLOBAL VARIABLES !!! # rubocop:disable Style/GlobalVars Celluloid.logger.debug(decorate(string)) if $CELLULOID_DEBUG # rubocop:enable Style/GlobalVars end def info(string) Celluloid.logger.info(decorate(string)) end def warn(string) Celluloid.logger.warn(decorate(string)) end def error(string) Celluloid.logger.error(decorate(string)) end def decorate(string) [string, @backtrace].join("\n\t") end end @exception_handlers = [] module_function def with_backtrace(backtrace) yield WithBacktrace.new(backtrace) if Celluloid.logger end # Send a debug message def debug(string) # !!! DO NOT INTRODUCE ADDITIONAL GLOBAL VARIABLES !!! # rubocop:disable Style/GlobalVars Celluloid.logger.debug(string) if Celluloid.logger && $CELLULOID_DEBUG # rubocop:enable Style/GlobalVars end # Send a info message def info(string) Celluloid.logger.info(string) if Celluloid.logger end # Send a warning message def warn(string) Celluloid.logger.warn(string) if Celluloid.logger end # Send an error message def error(string) Celluloid.logger.error(string) if Celluloid.logger end # Handle a crash def crash(string, exception) if Celluloid.log_actor_crashes string << "\n" << format_exception(exception) error string end @exception_handlers.each do |handler| begin handler.call(exception) rescue => ex error "EXCEPTION HANDLER CRASHED:\n" << format_exception(ex) end end end # Note a deprecation def deprecate(message) trace = caller.join("\n\t") warn "DEPRECATION WARNING: #{message}\n\t#{trace}" end # Define an exception handler # NOTE: These should be defined at application start time def exception_handler(&block) @exception_handlers << block nil end # Format an exception message def format_exception(exception) str = "#{exception.class}: #{exception}\n\t" str << if exception.backtrace exception.backtrace.join("\n\t") else "EMPTY BACKTRACE\n\t" end end end end end celluloid-0.18.0/lib/celluloid/internals/stack.rb0000644000004100000410000000370414005764144021770 0ustar www-datawww-datamodule Celluloid module Internals class Stack attr_accessor :actors, :threads def initialize(threads) @group = threads @actors = [] @threads = [] end def snapshot(backtrace = nil) @group.each do |thread| if thread.role == :actor @actors << snapshot_actor(thread.actor, backtrace) if thread.actor else @threads << snapshot_thread(thread, backtrace) end end end def snapshot_actor(actor, backtrace = nil) state = ActorState.new state.id = actor.object_id # TODO: delegate to the behavior state.cell = snapshot_cell(actor.behavior) if actor.behavior.is_a?(Cell) tasks = actor.tasks if tasks.empty? state.status = :idle else state.status = :running state.tasks = tasks.to_a.map { |t| TaskState.new(t.class, t.type, t.meta, t.status, t.backtrace) } end state.backtrace = actor.thread.backtrace if backtrace && actor.thread state end def snapshot_cell(behavior) state = CellState.new state.subject_id = behavior.subject.object_id state.subject_class = behavior.subject.class state end def snapshot_thread(thread, backtrace = nil) if backtrace backtrace = begin thread.backtrace rescue NoMethodError # for Rubinius < 2.5.2.c145 [] end end ThreadState.new(thread.object_id, backtrace, thread.role) end def print(output = STDERR) @actors.each do |actor| output.print actor.dump end @threads.each do |thread| output.print thread.dump end end end end end require "celluloid/internals/stack/states" require "celluloid/internals/stack/dump" require "celluloid/internals/stack/summary" celluloid-0.18.0/lib/celluloid/internals/registry.rb0000644000004100000410000000520514005764144022531 0ustar www-datawww-datamodule Celluloid module Internals # The Registry allows us to refer to specific actors by human-meaningful names class Registry def initialize @root = nil # keep root out of the standard list of registered names @actors = {} # hash of name => actor @index = {} # hash of name => branch @branches = {} # hash of branch => [ actors ] @registry = Mutex.new end # Register an Actor def []=(name, actor) if name == :root @registry.synchronize do @root = actor end else actor_singleton = class << actor; self; end raise TypeError, "not an actor" unless actor_singleton.ancestors.include? Proxy::Abstract # if actor.class.ancestors.include? Supervision::Container # puts "Supervisor: #{actor.links.inspect}" # end @registry.synchronize do @actors[name.to_sym] = actor end actor.mailbox << NamingRequest.new(name.to_sym) end end def add(name, actor, branch = :services) set(name, actor) @registry.synchronize do unless @branches.key? branch @branches[branch] = [] self.class.instance_eval do begin remove_method(branch) rescue nil end define_method(branch) { @branches[branch] } end @branches[branch] << name end @index[name.to_sym] = branch end end # Retrieve an actor by name def [](name) return @root if name == :root @registry.synchronize do @actors[name.to_sym] end end def branch(name) @registry.synchronize do @index.select { |_a, b| b == name } end end alias get [] alias set []= def delete(name) @registry.synchronize do @index.delete name.to_sym @actors.delete name.to_sym end end def include?(name) names.include? name end # List all registered actors by name def names @registry.synchronize { @actors.keys } end def index @registry.synchronize { @index } end # removes and returns all registered actors as a hash of `name => actor` # can be used in testing to clear the registry def clear hash = nil @registry.synchronize do hash = @actors.dup @actors.clear @index.clear end hash end end end end celluloid-0.18.0/lib/celluloid/probe.rb0000644000004100000410000000354314005764144017774 0ustar www-datawww-datarequire "celluloid" # !!! DO NOT INTRODUCE ADDITIONAL GLOBAL VARIABLES !!! # rubocop:disable Style/GlobalVars $CELLULOID_MONITORING = true # rubocop:enable Style/GlobalVars module Celluloid class Probe include Celluloid include Celluloid::Notifications NOTIFICATIONS_TOPIC_BASE = "celluloid.events.%s".freeze EVENTS_BUFFER = Queue.new class << self def run # spawn the actor if not found supervise_as(:probe_actor) unless Actor[:probe_actor] && Actor[:probe_actor].alive? end def run_without_supervision Actor[:probe_actor] = Celluloid::Probe.new end def actor_created(actor) trigger_event(:actor_created, actor) end def actor_named(actor) trigger_event(:actor_named, actor) end def actor_died(actor) trigger_event(:actor_died, actor) end def actors_linked(a, b) a = find_actor(a) b = find_actor(b) trigger_event(:actors_linked, a, b) end private def trigger_event(name, *args) # !!! DO NOT INTRODUCE ADDITIONAL GLOBAL VARIABLES !!! # rubocop:disable Style/GlobalVars return unless $CELLULOID_MONITORING # rubocop:enable Style/GlobalVars EVENTS_BUFFER << [name, args] probe_actor = Actor[:probe_actor] probe_actor.async.process_queue if probe_actor end def find_actor(obj) if obj.__send__(:class) == Actor obj elsif owner = obj.instance_variable_get(OWNER_IVAR) owner end end end def initialize async.process_queue end def process_queue until EVENTS_BUFFER.empty? event = EVENTS_BUFFER.pop dispatch_event(*event) end end def dispatch_event(cmd, args) publish(NOTIFICATIONS_TOPIC_BASE % cmd, args) end end end celluloid-0.18.0/lib/celluloid/pool.rb0000644000004100000410000000026714005764144017636 0ustar www-datawww-datarequire "celluloid" unless defined? Celluloid require "celluloid/supervision" require "celluloid/supervision/container/pool" require "celluloid/supervision/container/behavior/pool" celluloid-0.18.0/lib/celluloid/call/0000755000004100000410000000000014005764144017246 5ustar www-datawww-datacelluloid-0.18.0/lib/celluloid/call/async.rb0000644000004100000410000000115714005764144020714 0ustar www-datawww-datamodule Celluloid class Call # Asynchronous calls don't wait for a response class Async < Call def dispatch(obj) Internals::CallChain.current_id = Celluloid.uuid super(obj) rescue AbortError => ex # Swallow aborted async calls, as they indicate the sender made a mistake # rubocop:disable Metrics/LineLength Internals::Logger.debug("#{obj.class}: async call `#{@method}` aborted!\n#{Internals::Logger.format_exception(ex.cause)}") # rubocop:enable Metrics/LineLength ensure Internals::CallChain.current_id = nil end end end end celluloid-0.18.0/lib/celluloid/call/block.rb0000644000004100000410000000077314005764144020674 0ustar www-datawww-datamodule Celluloid class Call class Block def initialize(block_proxy, sender, arguments, task = Thread.current[:celluloid_task]) @block_proxy = block_proxy @sender = sender @arguments = arguments @task = task end attr_reader :task def call @block_proxy.call end def dispatch response = @block_proxy.block.call(*@arguments) @sender << Internals::Response::Block.new(self, response) end end end end celluloid-0.18.0/lib/celluloid/call/sync.rb0000644000004100000410000000427114005764144020553 0ustar www-datawww-datamodule Celluloid class Call # Synchronous calls wait for a response class Sync < Call attr_reader :sender, :task, :chain_id def initialize( sender, method, arguments = [], block = nil, task = Thread.current[:celluloid_task], chain_id = Internals::CallChain.current_id ) super(method, arguments, block) @sender = sender @task = task @chain_id = chain_id || Celluloid.uuid end def dispatch(obj) Internals::CallChain.current_id = @chain_id result = super(obj) respond Internals::Response::Success.new(self, result) rescue ::Exception => ex # Exceptions that occur during synchronous calls are reraised in the # context of the sender respond Internals::Response::Error.new(self, ex) # Aborting indicates a protocol error on the part of the sender # It should crash the sender, but the exception isn't reraised # Otherwise, it's a bug in this actor and should be reraised raise unless ex.is_a?(AbortError) ensure Internals::CallChain.current_id = nil end def cleanup exception = DeadActorError.new("attempted to call a dead actor: #{method}") respond Internals::Response::Error.new(self, exception) end def respond(message) @sender << message end def response Celluloid.suspend(:callwait, self) end def value response.value end def wait loop do message = Celluloid.mailbox.receive do |msg| msg.respond_to?(:call) && msg.call == self end if message.is_a?(SystemEvent) Thread.current[:celluloid_actor].handle_system_event(message) else # FIXME: add check for receiver block execution if message.respond_to?(:value) # FIXME: disable block execution if on :sender and (exclusive or outside of task) # probably now in Call return message else message.dispatch end end end end end end end celluloid-0.18.0/lib/celluloid/rspec.rb0000644000004100000410000000256014005764144017777 0ustar www-datawww-datarequire "celluloid/test" module Specs CHECK_LOOSE_THREADS = false ALLOW_RETRIES = 3 unless defined? ALLOW_RETRIES INCLUDE_SUPPORT = %w[ logging sleep_and_wait reset_class_variables crash_checking loose_threads stubbing coverage includer configure_rspec ].freeze INCLUDE_PATHS = [ "./spec/support/*.rb", "./spec/support/examples/*.rb", "./spec/shared/*.rb" ].freeze MAX_EXECUTION = 13 MAX_ATTEMPTS = 20 TIMER_QUANTUM = 0.05 # Timer accuracy enforced by the tests (50ms) BACKTRACE_OMITTED = [ "rspec-expectations", "rspec-core", "rspec-mocks", "rspec-retry", "rubysl-thread", "rubysl-timeout" ].freeze end # !!! DO NOT INTRODUCE ADDITIONAL GLOBAL VARIABLES !!! # rubocop:disable Style/GlobalVars $CELLULOID_DEBUG = true # Require but disable, so it has to be explicitly enabled in tests require "celluloid/probe" $CELLULOID_MONITORING = false # rubocop:enable Style/GlobalVars Celluloid.shutdown_timeout = 1 # Load shared examples and test support code for other gems to use. Specs::INCLUDE_SUPPORT.each do |f| require "#{File.expand_path('../../spec/support', __dir__)}/#{f}.rb" end Specs.reset_probe(nil) Dir["#{File.expand_path('../../spec/support/examples', __dir__)}/*.rb"].map { |f| require f } Dir["#{File.expand_path('../../spec/shared', __dir__)}/*.rb"].map { |f| require f } celluloid-0.18.0/lib/celluloid.rb0000644000004100000410000003433414005764144016667 0ustar www-datawww-data# TODO: eliminate use of global variables require "English" require "logger" require "set" require "timeout" # !!! DO NOT INTRODUCE ADDITIONAL GLOBAL VARIABLES !!! # rubocop:disable Style/GlobalVars $CELLULOID_DEBUG = false $CELLULOID_MONITORING = false # rubocop:enable Style/GlobalVars require "celluloid/version" module Celluloid # Expose all instance methods as singleton methods extend self # Linking times out after 5 seconds LINKING_TIMEOUT = 5 # Warning message added to Celluloid objects accessed outside their actors BARE_OBJECT_WARNING_MESSAGE = "WARNING: BARE CELLULOID OBJECT ".freeze class << self attr_writer :actor_system # Default Actor System attr_accessor :logger # Thread-safe logger class attr_accessor :log_actor_crashes attr_accessor :group_class # Default internal thread group to use attr_accessor :task_class # Default task type to use attr_accessor :shutdown_timeout # How long actors have to terminate def actor_system if Thread.current.celluloid? Thread.current[:celluloid_actor_system] || raise(Error, "actor system not running") else Thread.current[:celluloid_actor_system] || @actor_system || raise(Error, "Celluloid is not yet started; use Celluloid.boot") end end def included(klass) klass.send :extend, ClassMethods klass.send :include, InstanceMethods klass.send :extend, Internals::Properties klass.property :mailbox_class, default: Celluloid::Mailbox klass.property :proxy_class, default: Celluloid::Proxy::Cell klass.property :task_class, default: Celluloid.task_class klass.property :group_class, default: Celluloid.group_class klass.property :mailbox_size klass.property :exclusive_actor, default: false klass.property :exclusive_methods, multi: true klass.property :execute_block_on_receiver, default: %i[after every receive], multi: true klass.property :finalizer klass.property :exit_handler_name singleton = class << klass; self; end begin singleton.send(:remove_method, :trap_exit) rescue nil end begin singleton.send(:remove_method, :exclusive) rescue nil end singleton.send(:define_method, :trap_exit) do |*args| exit_handler_name(*args) end singleton.send(:define_method, :exclusive) do |*args| if args.any? exclusive_methods(*exclusive_methods, *args) else exclusive_actor true end end end # Are we currently inside of an actor? def actor? !!Thread.current[:celluloid_actor] end # Retrieve the mailbox for the current thread or lazily initialize it def mailbox Thread.current[:celluloid_mailbox] ||= Celluloid::Mailbox.new end # Generate a Universally Unique Identifier def uuid Internals::UUID.generate end # Obtain the number of CPUs in the system def cores Internals::CPUCounter.cores end alias cpus cores alias ncpus cores # Perform a stack dump of all actors to the given output object def stack_dump(output = STDERR) actor_system.stack_dump.print(output) end alias dump stack_dump # Perform a stack summary of all actors to the given output object def stack_summary(output = STDERR) actor_system.stack_summary.print(output) end alias summarize stack_summary def public_registry actor_system.public_registry end # Detect if a particular call is recursing through multiple actors def detect_recursion actor = Thread.current[:celluloid_actor] return unless actor task = Thread.current[:celluloid_task] return unless task chain_id = Internals::CallChain.current_id actor.tasks.to_a.any? { |t| t != task && t.chain_id == chain_id } end # Define an exception handler for actor crashes def exception_handler(&block) Internals::Logger.exception_handler(&block) end def suspend(status, waiter) task = Thread.current[:celluloid_task] if task && !Celluloid.exclusive? waiter.before_suspend(task) if waiter.respond_to?(:before_suspend) Task.suspend(status) else waiter.wait end end def boot init start end def init @actor_system ||= Actor::System.new end def start actor_system.start end def running? actor_system && actor_system.running? rescue Error false end # de TODO Anticipate outside process finalizer that would by-pass this. def register_shutdown return if defined?(@shutdown_registered) && @shutdown_registered # Terminate all actors at exit, unless the exit is abnormal. at_exit do Celluloid.shutdown unless $ERROR_INFO end @shutdown_registered = true end # Shut down all running actors def shutdown actor_system.shutdown @actor_system = nil end def version VERSION end end # Class methods added to classes which include Celluloid module ClassMethods def new(*args, &block) proxy = Cell.new(allocate, behavior_options, actor_options).proxy proxy._send_(:initialize, *args, &block) proxy end alias spawn new # Create a new actor and link to the current one def new_link(*args, &block) raise NotActorError, "can't link outside actor context" unless Celluloid.actor? proxy = Cell.new(allocate, behavior_options, actor_options).proxy Actor.link(proxy) proxy._send_(:initialize, *args, &block) proxy end alias spawn_link new_link # Run an actor in the foreground def run(*args, &block) Actor.join(new(*args, &block)) end def actor_system Celluloid.actor_system end # Configuration options for Actor#new def actor_options { actor_system: actor_system, mailbox_class: mailbox_class, mailbox_size: mailbox_size, task_class: task_class, exclusive: exclusive_actor } end def behavior_options { proxy_class: proxy_class, exclusive_methods: exclusive_methods, exit_handler_name: exit_handler_name, finalizer: finalizer, receiver_block_executions: execute_block_on_receiver } end def ===(other) other.is_a? self end end # These are methods we don't want added to the Celluloid singleton but to be # defined on all classes that use Celluloid module InstanceMethods # Obtain the bare Ruby object the actor is wrapping. This is useful for # only a limited set of use cases like runtime metaprogramming. Interacting # directly with the bare object foregoes any kind of thread safety that # Celluloid would ordinarily provide you, and the object is guaranteed to # be shared with at least the actor thread. Tread carefully. # # Bare objects can be identified via #inspect output: # # >> actor # => # # >> actor.bare_object # => # # def bare_object self end alias wrapped_object bare_object # Are we being invoked in a different thread from our owner? def leaked? @celluloid_owner != Thread.current[:celluloid_actor] end def tap yield current_actor current_actor end # Obtain the name of the current actor def registered_name Actor.registered_name end alias name registered_name def inspect return "..." if Celluloid.detect_recursion str = "#<" str << if leaked? Celluloid::BARE_OBJECT_WARNING_MESSAGE else "Celluloid::Proxy::Cell" end str << "(#{self.class}:0x#{object_id.to_s(16)})" str << " " unless instance_variables.empty? instance_variables.each do |ivar| next if ivar == Celluloid::OWNER_IVAR str << "#{ivar}=#{instance_variable_get(ivar).inspect} " end str.sub!(/\s$/, ">") end def __arity method(:initialize).arity end end # # The following methods are available on both the Celluloid singleton and # directly inside of all classes that include Celluloid # # Raise an exception in sender context, but stay running def abort(cause) cause = case cause when String then RuntimeError.new(cause) when Exception then cause else raise TypeError, "Exception object/String expected, but #{cause.class} received" end raise AbortError, cause end # Terminate this actor def terminate Thread.current[:celluloid_actor].behavior_proxy.terminate! end # Send a signal with the given name to all waiting methods def signal(name, value = nil) Thread.current[:celluloid_actor].signal name, value end # Wait for the given signal def wait(name) Thread.current[:celluloid_actor].wait name end # Obtain the current_actor def current_actor Actor.current end # Obtain the UUID of the current call chain def call_chain_id Internals::CallChain.current_id end # Obtain the running tasks for this actor def tasks Thread.current[:celluloid_actor].tasks.to_a end # Obtain the Celluloid::Links for this actor def links Thread.current[:celluloid_actor].links end # Watch for exit events from another actor def monitor(actor) Actor.monitor(actor) end # Stop waiting for exit events from another actor def unmonitor(actor) Actor.unmonitor(actor) end # Link this actor to another, allowing it to crash or react to errors def link(actor) Actor.link(actor) end # Remove links to another actor def unlink(actor) Actor.unlink(actor) end # Are we monitoring another actor? def monitoring?(actor) Actor.monitoring?(actor) end # Is this actor linked to another? def linked_to?(actor) Actor.linked_to?(actor) end # Receive an asynchronous message via the actor protocol def receive(timeout = nil, &block) actor = Thread.current[:celluloid_actor] if actor actor.receive(timeout, &block) else Celluloid.mailbox.receive(timeout, &block) end end # Sleep letting the actor continue processing messages def sleep(interval) actor = Thread.current[:celluloid_actor] if actor actor.sleep(interval) else Kernel.sleep interval end end # Timeout on task suspension (eg Sync calls to other actors) def timeout(duration) Thread.current[:celluloid_actor].timeout(duration) do yield end end # Run given block in an exclusive mode: all synchronous calls block the whole # actor, not only current message processing. def exclusive(&block) Thread.current[:celluloid_task].exclusive(&block) end # Are we currently exclusive def exclusive? task = Thread.current[:celluloid_task] task && task.exclusive? end # Call a block after a given interval, returning a Celluloid::Timer object def after(interval, &block) Thread.current[:celluloid_actor].after(interval, &block) end # Call a block every given interval, returning a Celluloid::Timer object def every(interval, &block) Thread.current[:celluloid_actor].every(interval, &block) end # Perform a blocking or computationally intensive action inside an # asynchronous group of threads, allowing the sender to continue processing other # messages in its mailbox in the meantime def defer(&block) # This implementation relies on the present implementation of # Celluloid::Future, which uses a thread from InternalPool to run the block Future.new(&block).value end # Handle async calls within an actor itself def async(meth = nil, *args, &block) Thread.current[:celluloid_actor].behavior_proxy.async meth, *args, &block end # Handle calls to future within an actor itself def future(meth = nil, *args, &block) Thread.current[:celluloid_actor].behavior_proxy.future meth, *args, &block end end require "celluloid/exceptions" Celluloid.logger = Logger.new(STDERR).tap do |logger| # !!! DO NOT INTRODUCE ADDITIONAL GLOBAL VARIABLES !!! # rubocop:disable Style/GlobalVars logger.level = Logger::INFO unless $CELLULOID_DEBUG # rubocop:enable Style/GlobalVars end Celluloid.shutdown_timeout = 10 Celluloid.log_actor_crashes = true require "celluloid/calls" require "celluloid/condition" require "celluloid/thread" require "celluloid/core_ext" require "celluloid/system_events" require "celluloid/proxies" require "celluloid/mailbox" require "celluloid/mailbox/evented" require "celluloid/group" require "celluloid/group/spawner" require "celluloid/group/pool" # TODO: Find way to only load this if being used. require "celluloid/task" require "celluloid/task/fibered" require "celluloid/task/threaded" # TODO: Find way to only load this if being used. require "celluloid/actor" require "celluloid/cell" require "celluloid/future" require "celluloid/internals/call_chain" require "celluloid/internals/cpu_counter" require "celluloid/internals/handlers" require "celluloid/internals/links" require "celluloid/internals/logger" require "celluloid/internals/method" require "celluloid/internals/properties" require "celluloid/internals/receivers" require "celluloid/internals/registry" require "celluloid/internals/responses" require "celluloid/internals/signals" require "celluloid/internals/stack" require "celluloid/internals/task_set" require "celluloid/internals/thread_handle" require "celluloid/internals/uuid" require "celluloid/notifications" require "celluloid/supervision" require "celluloid/logging" require "celluloid/actor/system" # Configure default systemwide settings Celluloid.task_class = begin str = ENV["CELLULOID_TASK_CLASS"] || "Fibered" Kernel.const_get(str) rescue NameError begin Celluloid.const_get(str) rescue NameError Celluloid::Task.const_get(str) end end Celluloid.group_class = begin str = ENV["CELLULOID_GROUP_CLASS"] || "Spawner" Kernel.const_get(str) rescue NameError begin Celluloid.const_get(str) rescue NameError Celluloid::Group.const_get(str) end end celluloid-0.18.0/CONTRIBUTING.md0000644000004100000410000000526614005764144016053 0ustar www-datawww-data# Contribution Guidelines If you are seeking support, or for discussions about Celluloid, you can use [the mailing list](http://groups.google.com/group/celluloid-ruby) or the IRC channel, #celluloid on freenode. If you encounter an issue with Celluloid itself, you should go through the following checklist: * Is this a known bug or are you falling into a common trap? Check the [Gotchas wiki page](https://github.com/celluloid/celluloid/wiki/Gotchas). * Is there already an issue filed which looks like your issue? Check the [issue tracker](https://github.com/celluloid/celluloid/issues). * Is the problem present in the latest released version of Celluloid? Upgrade and check! * Is the problem present on the master branch of Celluloid? [Run pre-release](#running-pre-release-celluloid) from source control and check! If you don't get anywhere with this checklist, please feel free to [file a bug report](#filing-a-bug-report). ## Running pre-release Celluloid If you encounter a bug, it's entirely possible that it has already been fixed but not yet included in a released version. You can establish this by trying to run your application with a pre-release version of Celluloid direct from source control. You can do this by modifying your application's Gemfile as follows: ```ruby gem 'celluloid', github: 'celluloid', submodules: true ``` If it is suggested to you that you try a different branch, add `branch: 'somebranch'`. If the problem is resolved, feel free to voice your desire for a new release of Celluloid on IRC (`irc.freenode.net/#celluloid`). If it persists, you should consider [filing a bug report](#filing-a-bug-report). ## Filing a bug report * Bug reports should be filed on the [GitHub issue tracker](https://github.com/celluloid/celluloid/issues). Bug reports should contain the following things: * A sensible subject that helps quickly identify the issue. * Full steps to reproduce the issue, including minimal reproduction code. A minimal reproduction means only what is necessary to display the problem and nothing more. This is perhaps the most important thing, don't skip it! * Output from a reproduction. * Full references for version numbers (of Celluloid, dependencies, Ruby, Operating System, etc). One easy way to do this is to post your Gemfile.lock, though you will still need to tell us what version of Ruby is in use. * See: [Triage Process](https://github.com/celluloid/celluloid/wiki/Triage-Process) * Some more guidelines on filing good bug reports: * http://www.chiark.greenend.org.uk/~sgtatham/bugs.html * http://itscommonsensestupid.blogspot.com/2008/07/tips-to-write-good-bug-report.html * http://timheuer.com/blog/archive/2011/10/12/anatomy-of-a-good-bug-report.aspx celluloid-0.18.0/REFACTOR.md0000644000004100000410000000012414005764144015335 0ustar www-datawww-data20200703113000: `require "celluloid/current"` is removed. use `require "celluloid"` celluloid-0.18.0/CHANGES.md0000644000004100000410000006050514005764144015211 0ustar www-datawww-data## 0.18.0 (2020-12-05) [0.18.0]: https://github.com/celluloid/celluloid/compare/v0.17.3...v0.18.0 * [#804](https://github.com/celluloid/celluloid/pull/804) Remove deprecation warnings * [#802](https://github.com/celluloid/celluloid/pull/802) Remove lattice link itself. * [#797](https://github.com/celluloid/celluloid/pull/797) Update Ruby versions in CI script * [#801](https://github.com/celluloid/celluloid/pull/801) Associate Open Collective sponsorship option * [#800](https://github.com/celluloid/celluloid/pull/800) Streamline README * [#799](https://github.com/celluloid/celluloid/pull/799) Update links, add diagram preview. * [#796](https://github.com/celluloid/celluloid/pull/796) Create Class Diagram in Draw.io * [#798](https://github.com/celluloid/celluloid/pull/798) Pertinent Newsflash * [#792](https://github.com/celluloid/celluloid/pull/792) CI: 2.5.5, 2.6.2 * [#788](https://github.com/celluloid/celluloid/pull/788) Travis: Include Ruby 2.5, 2.6 * [#787](https://github.com/celluloid/celluloid/pull/787) Travis config: drop old configuration sudo: false * [#786](https://github.com/celluloid/celluloid/pull/786) Travis: use jruby-9.2.5.0 * [#783](https://github.com/celluloid/celluloid/pull/783) v0.18.0.pre2 * [#782](https://github.com/celluloid/celluloid/pull/782) Merge 'celluloid-supervision' back into the tree * [#781](https://github.com/celluloid/celluloid/pull/781) .gitmodules: No longer used * [#780](https://github.com/celluloid/celluloid/pull/780) README.md: Link to unmaintained issue * [#778](https://github.com/celluloid/celluloid/pull/778) README.md: Add "maintained: no! (as of 2016)" badge * [#777](https://github.com/celluloid/celluloid/pull/777) gemspec: Metadata with supported links * [#776](https://github.com/celluloid/celluloid/pull/776) Travis: use jruby-9.2.0.0 * [#775](https://github.com/celluloid/celluloid/pull/775) Travis: jruby-9.1.17.0 * [#769](https://github.com/celluloid/celluloid/pull/769) Travis: jruby-9.1.15.0 * [#768](https://github.com/celluloid/celluloid/pull/768) Travis: use latest JRuby * [#767](https://github.com/celluloid/celluloid/pull/767) CHANGES: Add GitHub compare link on each heading * [#766](https://github.com/celluloid/celluloid/pull/766) Fix celluloid/celluloid#758 * [#765](https://github.com/celluloid/celluloid/pull/765) Travis: jruby-9.1.13.0 * [#761](https://github.com/celluloid/celluloid/pull/761) Travis: jruby-9.1.12.0 * [#760](https://github.com/celluloid/celluloid/pull/760) Travis: jruby-9.1.10.0 * [#759](https://github.com/celluloid/celluloid/pull/759) Travis: jruby-9.1.9.0 * [#757](https://github.com/celluloid/celluloid/pull/757) parameterize error message so old and new Rubies match text * [#756](https://github.com/celluloid/celluloid/pull/756) Travis: latest stable rubies * [#754](https://github.com/celluloid/celluloid/pull/754) README: Fix badge rendering in Markdown * [#753](https://github.com/celluloid/celluloid/pull/753) Travis: use jruby-9.1.8.0 * [#752](https://github.com/celluloid/celluloid/pull/752) Misspellings * [#749](https://github.com/celluloid/celluloid/pull/749) Return false from Celluloid.running? if Celluloid.boot hasn't been called * [#751](https://github.com/celluloid/celluloid/pull/751) Travis: Use JRuby 9.1.7.0 * [#740](https://github.com/celluloid/celluloid/pull/740) Global variables: stop the bleeding * [#739](https://github.com/celluloid/celluloid/pull/739) Remove hacks around old MRIs, JRuby, and rbx * [#738](https://github.com/celluloid/celluloid/pull/738) Update to RuboCop 0.45.0 (with new rubocop.yml policy) * [#737](https://github.com/celluloid/celluloid/pull/737) Simplify dependencies: merge 'essentials' and 'culture' repos * [#736](https://github.com/celluloid/celluloid/pull/736) Remove $CELLULOID_BACKPORTED and $CELLULOID_MANAGED * [#735](https://github.com/celluloid/celluloid/pull/735) Require Ruby 2.2.6+ * [#729](https://github.com/celluloid/celluloid/pull/729) Remove mysterious Fiber.yield * [#721](https://github.com/celluloid/celluloid/pull/721) Instruction for cloning Celluloid via github * [#715](https://github.com/celluloid/celluloid/pull/715) fix error response reference in Future#cancel * [#712](https://github.com/celluloid/celluloid/pull/712) Add RBX-3 to the build * [#711](https://github.com/celluloid/celluloid/pull/711) Added bundler cache * [#709](https://github.com/celluloid/celluloid/pull/709) Fix autostart. Fixes https://github.com/celluloid/celluloid/issues/698 * [#705](https://github.com/celluloid/celluloid/pull/705) Adding method source code path to backtrace ## [0.17.3] (2016-01-18) [0.17.3]: https://github.com/celluloid/celluloid/compare/v0.17.2...v0.17.3 * [#701](https://github.com/celluloid/celluloid/pull/701) Conditions in loose threads loop does not take into account the difference between backtraces from ruby 2.0.0 and greater than. Fixes celluloid/celluloid-io#165. ([@TiagoCardoso1983]) * [#700](https://github.com/celluloid/celluloid/pull/700) Set celluloid logger level to info by default unless debug is enabled. Fixes #667. ([@ioquatix]) * [#695](https://github.com/celluloid/celluloid/pull/695) Extending the condition event handler with the block; this solves a bug introduced in jruby >9.0.0.0, which breaks with an ArgumentError exception, apparently due to the way to_proc procs are passed arguments. Fixes #694. ([@TiagoCardoso1983]) * [#689](https://github.com/celluloid/celluloid/pull/689) Simplified sync, async and future proxies by providing specific AbstractCall base. ([@ioquatix]) * [#688](https://github.com/celluloid/celluloid/pull/688) Fix failure to remove dead actors from sets, e.g. celluloid-supervision. ([@ioquatix]) * [#686](https://github.com/celluloid/celluloid/pull/686) Print out method name to help debugging method call which caused dead actor error. ([@ioquatix]) * [#682](https://github.com/celluloid/celluloid/pull/682) Remove excess call/block require. * [#666](https://github.com/celluloid/celluloid/pull/666) Don't catch IOError. ## [0.17.2] (2015-09-30) [0.17.2]: https://github.com/celluloid/celluloid/compare/v0.17.1.2...v0.17.2 * Revamped test suite, using shared RSpec configuration layer provided by Celluloid itself. * Updated gem dependencies provided by Celluloid::Sync... extraneous gems removed, or marked as development dependencies. * Clean up deprecation notes. ## [0.17.1.2] (2015-08-21) [0.17.1.2]: https://github.com/celluloid/celluloid/compare/v0.17.1.1...v0.17.1.2 * Fixes to posted markdown content. * Pull in new gem dependencies. ## [0.17.1.1] (2015-08-07) [0.17.1.1]: https://github.com/celluloid/celluloid/compare/v0.17.1...v0.17.1.1 * Revert "no task to suspend" code from #232. ## [0.17.1] (2015-08-06) [0.17.1]: https://github.com/celluloid/celluloid/compare/v0.17.0...v0.17.1 * `Celluloid::ActorSystem` moved to `Celluloid::Actor::System`, and from `celluloid/actor_system.rb` to `celluloid/actor/system.rb` * Added extensible API for defining new SystemEvents, and having them handled... without everyone changing `Actor#handle_system_event`. * Deprecated Task::TerminatedError & Task::TimeoutError... Consolidated in exceptions.rb, inherited from Exceptions vs. StandardError. * General round-up of all "errors" emitted throughout Celluloid, to either be derived from `Celluloid::Error` or `Celluloid::Interruption`. * Added ability to pass a block to `Condition#wait` which runs a `{ |value| ... }` type block if present, once the value is obtained by waiting. ## [0.17.0] (2015-07-04) [0.17.0]: https://github.com/celluloid/celluloid/compare/v0.16.0...v0.17.0 * Fix $CELLULOID_TEST warnings * Massive overhaul of test suite, end-to-end. * Make "Terminating task" log messages debug-level events * Added `.dead?` method on actors, as opposite of `.alive?` * Added class/module method to access `publish` outside actors. * Radical Refactor of Celluloid::InternalPool, and moved it to Celluloid::Group::Pool * Radical Refactor: *::Group::Pool replaced as default with *::Group::Spawner * Added `rspec-log_split` as replacement logger for itemized testing logs. * *::Task::PooledFibers has been found and made available, and compatible ( sometimes 4x faster than even Task::Fibered ) * GEM EXTRACTION: PoolManager taken out, and implemented in the `celluloid-pool` gem, separately. * GEM EXTRACTION: FSM taken out, and implemented in the `celluloid-fsm` gem, separately. * GEM EXTRACTION: SupervisionGroup, Supervisor, and related methods taken out, and implemented in the `celluloid-supervision` gem, separately. * BREAKING CHANGE: Added Celluloid::Internals and moved several "private" classes into that namespace: * CallChain, CPUCounter, Handlers ( and Handle ), Links, Logger, Method, Properties, Registry, Responses, Signals, StackDump, TaskSet, ThreadHandle, UUID. * BREAKING CHANGE: Changed class names, per convention: * Moved Celluloid::TaskFiber to Celluloid::Task::Fibered * Moved Celluloid::TaskThread to Celluloid::Task::Threaded * Moved Celluloid::EventedMailbox to Celluloid::Mailbox::Evented * Moved Celluloid::AbstractProxy to Celluloid::Proxy::Abstract * Moved Celluloid::ActorProxy to Celluloid::Proxy::Actor * Moved Celluloid::AsyncProxy to Celluloid::Proxy::Async * Moved Celluloid::BlockProxy to Celluloid::Proxy::Block * Moved Celluloid::CellProxy to Celluloid::Proxy::Cell * Moved Celluloid::FutureProxy to Celluloid::Proxy::Future * Moved Celluloid::SyncProxy to Celluloid::Proxy::Sync * GEM EXTRACTION: `Internals`, `Notifications`, `Probe`, and the contents of `logging/*` have become a `celluloid-essentials` gem. * Implement `Group::Manager` as base for future `Group::Unlocker` and other such systems traversing `ActorSystem#group` regularly. * Reduce number of supervisors instantiated by `ActorSystem` by consolidating them down to `Service::Root` container instances. ## [0.16.0] (2014-09-04) [0.16.0]: https://github.com/celluloid/celluloid/compare/v0.15.2...v0.16.0 * Factor apart Celluloid::Cell (concurrent objects) from Celluloid::Actor * Introduce Celluloid::ActorSystem as an abstraction around the backend actor implementation (idea borrowed from Akka) * Celluloid::Probe system for monitoring system behavior * Fix handling of timeouts with Celluloid::EventedMailbox (i.e. Celluloid::IO and Celluloid::ZMQ) * Add timeout support to Celluloid::Condition * Obtain actor names via Celluloid::Actor.registered_name and #registered_name to avoid conflicts with the built-in Ruby Class.name method * Update to timers 4.0.0 * Dynamically resizable pools * Remove use of core Ruby ThreadGroups * Simplified CPU core detector * Better thread names on JRuby for easier debugging * Thread safety fixes to internal thread pool ## [0.15.2] (2013-10-06) [0.15.2]: https://github.com/celluloid/celluloid/compare/v0.15.1...v0.15.2 * require 'celluloid/test' for at_exit-free testing ## [0.15.1] (2013-09-06) [0.15.1]: https://github.com/celluloid/celluloid/compare/v0.15.0...v0.15.1 * Only raise on nested tasks if $CELLULOID_DEBUG is set ## [0.15.0] (2013-09-04) [0.15.0]: https://github.com/celluloid/celluloid/compare/v0.14.0...v0.15.0 * Remove legacy support for "bang"-method based async invocation * Generic timeout support with Celluloid#timeout * Implement recursion detection for #inspect, avoiding infinite loop bugs * Fix various inheritance anomalies in class attributes (e.g. mailbox_class) * Avoid letting version.rb define an unusable Celluloid module * Remove "Shutdown completed cleanly" message that was annoying everyone * Subclass all Celluloid exceptions from Celluloid::Error * Log all unhandled messages * Celluloid::Conditions are now usable ubiquitously, not just inside actors * Introspection support for number of threads in the Celluloid thread pool * Use a ThreadGroup to track the threads in the Celluloid thread pool * Reimplement signal system on top of Conditions * Add metadata like the current method to Celluloid::StackDumps ## [0.14.0] (2013-05-07) [0.14.0]: https://github.com/celluloid/celluloid/compare/v0.13.0...v0.14.0 * Use a Thread-subclass for Celluloid * Implement actor-local variables * Add helper methods to the class * Move IO::Mailbox to EventedMailbox to remove dependency between celluloid-io and celluloid-zmq. This makes it easier to maintain the evented style of Mailbox. * Install the `at_exit` handler by default * Show backtrace for all tasks. Currently only for TaskThread * Implement mailbox bounds where overflow is logged * Fix Thread self-join * Execute blocks on the sender by default * Fix CPU counter on windows ## [0.13.0] [0.13.0]: https://github.com/celluloid/celluloid/compare/v0.12.4...v0.13.0 * API change: Require Celluloid with: require 'celluloid/autostart' to automatically start support actors and configure at_exit handler which automatically terminates all actors. * API change: use_mailbox has been removed * API change: finalizers must be declared with "finalizer :my_finalizer" * Bugfix: receivers don't crash when methods are called incorrectly * Celluloid::Condition provides ConditionVariable-like signaling * Shutdown timeout reduced to 10 seconds * Stack traces across inter-actor calls! Should make Celluloid backtraces much easier to understand * Celluloid#call_chain_id provides UUIDs for calls across actors * Give all thread locals a :celluloid_* prefix ## [0.12.4] [0.12.4]: https://github.com/celluloid/celluloid/compare/v0.12.3...v0.12.4 * Bugfix: Clear dead/crashed actors out of links * Bugfix: Exclusive mode was broken * Bugfix: Celluloid::SupervisionGroup#run was broken * Celluloid::ClassMethods#proxy_class allows configurable proxies * Improved error messages for Fiber-related problems * Better object leakage detection when inspecting * Use #public_send to dispatch Celluloid methods * #idle_size and #busy_size for Celluloid::PoolManager ## [0.12.3] [0.12.3]: https://github.com/celluloid/celluloid/compare/v0.12.2...v0.12.3 * Bugfix: Ensure exclusive mode works correctly for per-method case * Bugfix: Exit handlers were not being inherited correctly ## [0.12.2] [0.12.2]: https://github.com/celluloid/celluloid/compare/v0.12.1...v0.12.2 * Disable IncidentReporter by default ## [0.12.1] [0.12.1]: https://github.com/celluloid/celluloid/compare/v0.12.0...v0.12.1 * Fix bug in unsetting of exclusive mode * New incident report system for providing better debugging reports * Revert BasicObject proxies for now... they are causing problems * String inspect that reveals bare object leaks * Fix bug reporting proper task statuses * Initial thread dumper support * Remove Celluloid#alive? as it cannot be called in any manner that will ever return anything but true, rendering it useless ## [0.12.0] [0.12.0]: https://github.com/celluloid/celluloid/compare/v0.11.1...v0.12.0 * Alternative async syntax: actor.async.method in lieu of actor.method! Original syntax still available but will be removed in Celluloid 1.0 * Alternative future syntax: actor.future.method in lieu of future(:method) * All methods in the Celluloid module are now available on its singleton * The #join and #kill methods are no longer available on the actor proxy. Please use Celluloid::Actor.join(actor) and .kill(actor) instead. * Celluloid::Future#ready? can be used to query for future readiness * Celluloid::Group constant removed. Please use Celluloid::SupervisionGroup * #monitor, #unmonitor, and #monitoring? provide unidirectional linking * Linking is now performed via a SystemEvent * SystemEvents are no longer exceptions. Boo exceptions as flow control! * Celluloid::Mailbox#system_event eliminated and replaced with Mailbox#<< SystemEvents are now automatically high priority * The task_class class method can be used to override the class used for tasks, allowing different task implementations to be configured on an actor-by-actor-basis * Celluloid::TaskThread provides tasks backed by Threads instead of Fibers * ActorProxy is now a BasicObject * A bug prevented Celluloid subclasses from retaining custom mailboxes defined by use_mailbox. This is now fixed. * `exclusive` class method without arguments makes the whole actor exclusive ## [0.11.1] [0.11.1]: https://github.com/celluloid/celluloid/compare/v0.11.0...v0.11.1 * 'exclusive' class method marks methods as always exclusive and runs them outside of a Fiber (useful if you need more stack than Fibers provide) * Celluloid::PoolManager returns its own class when #class is called, instead of proxying to a cell/actor in the pool. * #receive now handles SystemEvents internally * Celluloid::Timers extracted into the timers gem, which Celluloid now uses for its own timers ## [0.11.0] [0.11.0]: https://github.com/celluloid/celluloid/compare/v0.10.0...v0.11.0 * Celluloid::Application constant permanently removed * Celluloid::Pool removed in favor of Celluloid.pool * Celluloid::Group renamed to Celluloid::SupervisionGroup, old name is still available and has not been deprecated * Celluloid::ThreadPool renamed to Celluloid::InternalPool to emphasize its internalness * Support for asynchronously calling private methods inside actors * Future is now an instance method on all actors * Async call exception logs now contain the failed method * MyActor#async makes async calls for those who dislike the predicate syntax * abort can now accept a string instead of an exception object and will raise RuntimeError in the caller's context ## [0.10.0] [0.10.0]: https://github.com/celluloid/celluloid/compare/v0.9.1...v0.10.0 * Celluloid::Actor.current is now the de facto way to obtain the current actor * #terminate now uses system messages, making termination take priority over other pending methods * #terminate! provides asynchronous termination ## [0.9.1] [0.9.1]: https://github.com/celluloid/celluloid/compare/v0.9.0...v0.9.1 * Recurring timers with Celluloid#every(n) { ... } * Obtain UUIDs with Celluloid.uuid * Obtain the number of CPU cores available with Celluloid.cores * Celluloid::Pool defaults to one actor per CPU core max by default ## [0.9.0] [0.9.0]: https://github.com/celluloid/celluloid/compare/v0.8.0...v0.9.0 * Celluloid::Pool supervises pools of actors * Graceful shutdown which calls #terminate on all actors * Celluloid::Actor.all returns all running actors * Celluloid#exclusive runs a high priority block which prevents other methods from executing * Celluloid.exception_handler { |ex| ... } defines a callback for notifying exceptions (for use with Airbrake, exception_notifier, etc.) ## [0.8.0] [0.8.0]: https://github.com/celluloid/celluloid/compare/v0.7.2...v0.8.0 * Celluloid::Application is now Celluloid::Group * Futures no longer use a thread unless created with a block * No more future thread-leaks! Future threads auto-terminate now * Rename Celluloid#async to Celluloid#defer * Celluloid#tasks now returns an array of tasks with a #status attribute * Reduce coupling between Celluloid and DCell. Breaks compatibility with earlier versions of DCell. * Celluloid::FSMs are no longer actors themselves * Benchmarks using benchmark_suite ## [0.7.2] [0.7.2]: https://github.com/celluloid/celluloid/compare/v0.7.1...v0.7.2 * Workaround fiber problems on JRuby 1.6.5.1 in addition to 1.6.5 * Fix class displayed when inspecting dead actors ## [0.7.1] [0.7.1]: https://github.com/celluloid/celluloid/compare/v0.7.0...v0.7.1 * More examples! * Cancel all pending tasks when actors crash * Log all errors that occur during signaling failures * Work around thread-local issues on rbx (see 52325ecd) ## [0.7.0] [0.7.0]: https://github.com/celluloid/celluloid/compare/v0.6.2...v0.7.0 * Celluloid::Task abstraction replaces Celluloid::Fiber * Celluloid#tasks API to introspect on running tasks * Move Celluloid::IO into its own gem, celluloid-io * Finite state machines with Celluloid::FSM * Fix bugs in supervisors handling actors that crash during initialize * Old syntax Celluloid::Future() { ... } deprecated. Please use the #future method or Celluloid::Future.new { ... } to create futures * New timer subsystem! Bullet point-by-bullet point details below * Celluloid#after registers a callback to fire after a given time interval * Celluloid.sleep and Celluloid#sleep let an actor continue processing messages * Celluloid.receive and Celluloid#receive now accept an optional timeout * Celluloid::Mailbox#receive now accepts an optional timeout ## [0.6.2] [0.6.2]: https://github.com/celluloid/celluloid/compare/v0.6.1...v0.6.2 * List all registered actors with Celluloid::Actor.registered * All logging now handled through Celluloid::Logger * Rescue DeadActorError in Celluloid::ActorProxy#inspect ## [0.6.1] [0.6.1]: https://github.com/celluloid/celluloid/compare/v0.6.0...v0.6.1 * Celluloid#links obtains Celluloid::Links for a given actor * The #class method is now proxied to actors * Celluloid::Fiber replaces the Celluloid.fiber and Celluloid.resume_fiber API * Use Thread.mailbox instead of Thread.current.mailbox to obtain the mailbox for the current thread ## [0.6.0] [0.6.0]: https://github.com/celluloid/celluloid/compare/v0.5.0...v0.6.0 * Celluloid::Application classes for describing the structure of applications built with Celluloid * Methods of actors can now participate in the actor protocol directly via Celluloid#receive * Configure custom mailbox types using Celluloid.use_mailbox * Define a custom finalizer for an actor by defining MyActor#finalize * Actor.call and Actor.async API for making direct calls to mailboxes * Fix bugs in Celluloid::Supervisors which would crash on startup if the actor they're supervising also crashes on startup * Add Celluloid.fiber and Celluloid.resume_fiber to allow extension APIs to participate in the Celluloid fiber protocol ## [0.5.0] [0.5.0]: https://github.com/celluloid/celluloid/compare/v0.4.0...v0.5.0 * "include Celluloid::Actor" no longer supported. Use "include Celluloid" * New Celluloid::IO module for actors that multiplex IO operations * Major overhaul of Celluloid::Actor internals (see 25e22cc1) * Actor threads are pooled in Celluloid::Actor::Pool, improving the speed of creating short-lived actors by over 2X * Classes that include Celluloid now have a #current_actor instance method * Celluloid#async allows actors to make indefinitely blocking calls while still responding to messages * Fix a potential thread safety bug in Thread#mailbox * Experimental Celluloid::TCPServer for people wanting to write servers in Celluloid. This may wind up in another gem, so use at your own risk! * Magically skip ahead a few version numbers to impart the magnitude of this release. It's my versioning scheme and I can do what I wanna. ## [0.4.0] [0.4.0]: https://github.com/celluloid/celluloid/compare/v0.3.0...v0.4.0 * This version was mysteriously lost to the sands of time ## [0.3.0] [0.3.0]: https://github.com/celluloid/celluloid/compare/v0.2.2...v0.3.0 * This version was also mysteriously lost to the sands of time ## [0.2.2] [0.2.2]: https://github.com/celluloid/celluloid/compare/v0.2.1...v0.2.2 * AbortErrors now reraise in caller scope and get a caller-focused backtrace * Log failed async calls instead of just letting them fail silently * Properly handle arity of synchronous calls * Actors can now make async calls to themselves * Resolve crashes that occur when sending responses to exited/dead callers ## [0.2.1] [0.2.1]: https://github.com/celluloid/celluloid/compare/v0.2.0...v0.2.1 * Hack around a bug of an indeterminate cause (2baba3d2) ## [0.2.0] [0.2.0]: https://github.com/celluloid/celluloid/compare/v0.1.0...v0.2.0 * Support for future method calls with MyActor#future * Initial signaling support via MyActor#signal and MyActor#wait * Just "include Celluloid" works in lieu of "include Celluloid::Actor" * Futures terminate implicitly when their values are obtained * Add an underscore prefix to all of Celluloid's instance variables so they don't clash with user-defined ones. ## [0.1.0] [0.1.0]: https://github.com/celluloid/celluloid/compare/v0.0.3...v0.1.0 * Fiber-based reentrant actors. Requires Ruby 1.9 * MyActor.new (where MyActor includes Celluloid::Actor) is now identical to .spawn * Terminate actors with MyActor#terminate * Obtain current actor with Celluloid.current_actor * Configurable logger with Celluloid.logger * Synchronization now based on ConditionVariables instead of Celluloid::Waker * Determine if you're in actor scope with Celluloid.actor? ## [0.0.3] [0.0.3]: https://github.com/celluloid/celluloid/compare/v0.0.1...v0.0.3 * Remove self-referential dependency in gemspec ## 0.0.1 * Initial release [@ioquatix]: https://github.com/ioquatix [@TiagoCardoso1983]: https://github.com/TiagoCardoso1983 celluloid-0.18.0/LICENSE.txt0000644000004100000410000000206314005764144015435 0ustar www-datawww-dataCopyright (c) 2011-2016 Tony Arcieri, Donovan Keme Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.