Git Product home page Git Product logo

cpp-rotor's Introduction

Rotor

rotor is event loop friendly C++ actor micro framework, github abf gitee

telegram CircleCI appveyor codecov Codacy Badge license

features

  • minimalistic loop agnostic core
  • erlang-like hierarchical supervisors, see this and this
  • various event loops supported (wx, boost-asio, ev) or planned (uv, gtk, etc.)
  • asynchronous message passing interface
  • request-response messaging with cancellation capabilities, see
  • MPMC (multiple producers multiple consumers) messaging, aka pub-sub
  • cross-platform (windows, macosx, linux)
  • inspired by The Reactive Manifesto and sobjectizer

messaging performance

inter-thread (1) cross-thread (2) single thread (3)
~23.6M messages/second ~ 2.5M messages/second ~34.6М messages/second

Setup: Intel Core i7-8550U, Void Linux 5.15.

(1) Backend-independent; Can be measured with examples/boost-asio/ping-pong-single-simple, examples/ev/ping-pong-ev.

(2) Does not apply to wx-backend; can be measured with examples/thread/ping-pong-thread, examples/boost-asio/ping-pong-2-threads, examples/ev/ping-pong-ev-2-threads.

(3) Backend-independent inter-thread messaging when build with BUILD_THREAD_UNSAFE=True. rotor objects (messages and actors) cannot be accessed from different threads, cross-thread message sending facility cannot be used. This option is mainly targeted for single-threaded apps.

license

MIT

documentation

Please read tutorial, design principles and manual here

embedded

Looking for something actor-flavored, but suitable for embedded applications? Take a look into my rotor-light project.

Changelog

0.30 (23-Apr-2024)

  • [feature] added fltk-backend
  • [feature, conan] enable_fltk option which add fltk-support
  • [example] added /examples/ping-pong-fltk.cpp and examples/ping-pong-fltk_and_thread.cpp
  • [bugfix] wx-backend building and testing
  • [improvement, breaking] output directories are set to bin for cmake
  • [improvement, breaking] actor_base_t::make_error() is marked as const

0.29 (24-Feb-2024)

  • [bugfix] fix segfault in delivery plugin in debugging mode (try to set env ROTOR_INSPECT_DELIVERY=99 to see)

0.28 (22-Jan-2024)

  • [cmake, bugfix] add missing header into installation

0.27 (21-Jan-2024)

  • [feature] new interface message_visitor_t
  • [feature] new interface message_stringifier_t and the default implementation default_stringifier_t which allows to dump messages. It is not a production but a diagnostic/debug tool, due to performance restrictions.
  • [feature] system_context_t provides a reference to default message_stringifier_t; it is possible to have a custom one
  • [feature, breaking] extended_error_t holds a reference to a request message, which caused an error
  • [examples, tests, win32] fix ev examples and tests
  • [example] modernize examples/thread/sha512.cpp to use recent openssl version
  • [breaking] cmake requirements are lowered to 3.15
  • [breaking] fix minor compilation warnings

0.26 (08-Jan-2024)

  • [feature] start_timer callback not only method, but any invocable
  • [feature, conan] enable_ev option which add libev
  • [breaking, conan] boost minimum version 1.83.0
  • [testing, conan] remove catch2 from sources and make it dependencies
  • [bugfix, breaking] make plugins more dll-friendly
  • [breaking] cmake minimum version 3.23
  • [breaking] rename registry_t::revese_map_t revese_map -> registry_t::reverse_map_t reverse_map
  • [breaking] rename struct cancelation_t -> cancellation_t
  • [doc] fix multiple typos

0.25 (26-Dec-2022)

  • [bugfix] avoid response messages loose their order relative to regular message
  • [bugfix, example] add missing header

0.24 (04-Jun-2022)

  • [feature] improve inter-thread messaging performance up to 15% by using boost::unordered_map instead of std::unordered_map
  • [bugfix, breaking] avoid introducing unnecessary event loops latency by intensive polling of rotor queues; affects asio and ev loops
  • [bugfix] registry_plugin_t, allow to discover aliased services (#46)

0.23 (23-Apr-2022)

  • [bugfix] fix compilation issues of messages.cpp on some platforms
  • [bugfix, msvc] fix compilation issues of registry plugin for shared library on msvc-16+

0.22 (21-Apr-2022)

  • [feature] possibly to install via conan center
  • [feature, breaking] possibility to build rotor as shared library
  • [feature] add shutdown flag checker (see my blog)
  • [bugfix] requests do not outlive actors (i.e. they are cancelled on shutdown_finish)
  • [example] there is my another open-source project syncspirit, which uses rotor under hood. I recommend to look at it, if the shipped examples are too-trivial, and don't give you an architectural insight of using rotor.

0.21 (25-Mar-2022)

  • [improvement] preliminary support of conan package manager
  • [bugfix] fix compilation warnings on Windows/MSVC
  • [bugfix] add missing header for rotor::thread installation

0.20 (20-Feb-2022)

  • [improvement] superviser can create spawner, which has a policy to auto-spawns new actor instance if previous instance has been shutdown. This is similar to escalate_failure supervising in erlang, see dedicated article
  • [improvement] actor can now autoshutdown_supervisor(), when it shutdown
  • [improvement] actor can now escalate_failure(), i.e. trigger shutdown own supervisor when it finished with failure
  • [improvement] messages delivery order is persevered per-locality (see issue #41)
  • [example] examples/thread/ping-pong-spawner (new)
  • [example] examples/autoshutdown (new)
  • [example] examples/escalate-failure (new)
  • [documentation] updated Design principles
  • [documentation] updated Examples
  • [documentation] updated Introduction

0.19 (31-Dec-2021)

  • [improvement] performance improvement in inter-thread (+20%) and cross-thread messaging
  • [bugfix] supervisor does not shut self down in rare conditions, when it fails to initialize self
  • [bugfix] link_server plugin should ignore unlink_notifications
  • [bugfix] avoid cycle (i.e. memleak) in rare cases when supervisor is shutdown, but an external message arrives for processing

0.18 (03-Dec-2021)

  • [improvement] add static_assert for noexcept check of a hanler signature
  • [improvement] add gitee mirror
  • [bugfix] fix potential use-after-free in ev backend

0.17 (23-Oct-2021)

  • [bugfix] fix installation issues with cmake (thanks to @melpon)
  • [bugfix] fix missing header (thanks to @melpon)
  • [ci] drop travis-ci in the sake of circle-ci

0.16 (22-Aug-2021)

  • [improvement] significant message throughtput increase for std::thread, boost-asio and ev backends (upto 5x times)
  • [improvement] extended_error can now access to root reason
  • [improvement] delivery plugin in debug mode dumps discovery requests and responses
  • [improvement/breaking] more details on fatal error (system_context)
  • [example] examples/thread/ping-pong-thread.cpp (new)
  • [example] examples/ev/ping-pong-ev-2-threads (new)

0.15 (02-Apr-2021)

  • [bugfix] lifetime_plugin_t do not unsubscribe from foreign to me subscriptions
  • [bugfix] foreigners_support_plugin_t more safely deliver a message for a foreign subscriber (actor)

0.14 (20-Feb-2021)

  • the dedicated article with highlights: en and ru
  • [improvement] actor identity has been introduced. It can be configured or generated via address_maker plugin
  • [improvement] actor::do_shutdown() - optionally takes shutdown reason
  • [improvement/breaking] instead of using std::error_code the extended_error class is used. It wraps std::error_code, provides string context and pointer to the next extended_error cause. This greatly simplifies error tracking of errors. Every response now contains ee field instead of ec.
  • [improvement] actor has shutdown reason (in form of extended_error pointer)
  • [improvement] delivery plugin in debug mode it dumps shutdown reason in shutdown trigger messages
  • [improvement] actor identity has on_unlink method to get it know, when it has been unlinked from server actor
  • [improvement] add resources plugin for supervisor
  • [breaking] all responses now have extended_error pointer instead of std::error_code
  • [breaking] shutdown_request_t and shutdown_trigger_t messages how have shutdown reason (in form of extended_error pointer)
  • [bugfix] link_client_plugin_t do not invoke custom callback, before erasing request in case of failure
  • [bugfix] child_manager_plugin_t reactivate self if a child was created from other plugin.
  • [bugfix] registry actor incorrectly resolves postponed requests to wrong addresses

0.13 (26-Dec-2020)

  • [improvement] delivery plugin in debug mode dumps discarded messages
  • [breaking] state_response_t has been removed
  • [bugfix] allow to acquire & release resources in via resources_plugin_t, during other plugin configuration
  • [bugfix] foreigners_support_plugin_t did not deactivated self properly, caused assertion fail on supervisor shutdown, when there was foreign subscriptions
  • [bugfix] link_client_plugin_t did not notified linked server-actors, if its actor is going to shutdown; now server-actors are requested to unlink
  • [bugfix] starter_plugin_t sometimes crashed when subscription confirmation message arrives when actor is in non-initializing phase (i.e. shutting down)
  • [bugfix] root supervisor is not shutdown properly when it is linked as "server"

0.12 (08-Dec-2020)

  • [improvement] added std::thread backend (supervisor)
  • [bugfix] active timers, if any, are cancelled upon actor shutdown finish
  • [bugfix] supervisor shutdown message is lost in rare cases right after child actor start
  • [example] examples/thread/sha512.cpp (new)
  • [documentation] updated Event loops & platforms
  • [documentation] updated Patterns with Blocking I/O multiplexing
  • [deprecated] state_response_t, state_request_t will be removed in v0.13

0.11 (20-Nov-2020)

  • [improvement] when supervisor shuts self down due to child init failure, the supervisor init error code is "failure escalation"
  • [documentation] updated Advanced examples,
  • [bugfix] when actor shuts self down all its timers are properly cancelled
  • [bugfix] in rare case supervisor starts, event if child failed to init
  • [bugfix] asio: more correct timers cancellation implementation
  • [bugfix] ev: more correct shutdown (avoid memory leaks in rare cases)

0.10 (09-Nov-2020)

  • [improvement/breaking] Generic timers interface
  • [improvement] Request cancellation support
  • [improvement] added make_response methods when message should be created, but send later delayed
  • [improvement] more debug information in message delivery plugin
  • [documentation] Integration with event loops
  • [documentation] Requests cancellation and timers are demonstrated in the Advanced Examples section
  • [example] examples/boost-asio/ping-pong-timer.cpp (new)
  • [example] examples/boost-asio/beast-scrapper.cpp (updated)
  • [bugfix] avoid double configuration of a plugin in certain cases when interacting with resources plugin
  • [bugfix] more correct cmake installation (thanks to Jorge López Tello, @LtdJorge)

0.09 (03-Oct-2020)

  • the dedicated article with highlights: en and ru
  • [improvement] rewritten whole documentation
  • [improvement/breaking] plugin system where introduced for actors instead of behaviors
  • [improvement] actor_config_t was introduced, which now holds pointer to supervisor, init and shutdown timeouts
  • [improvement] builder pattern was introduced to simplify actors construction
  • [breaking] supervisor_config_t was changed (inherited from actor_config_t)
  • [breaking] actor_base_t and supervisor_t constructors has changed - now appropriate config is taken as single parameter. All descendant classes should be changed
  • [breaking] if a custom config type is used for actors/supervisors, they should define config_t inside the class, and templated config_builder_t.
  • [breaking] supervisor in actor is now accessible via pointer instead of reference
  • [bugfix] supervisor_ev_t not always correctly released EV-resources, which lead to leak
  • [bugfix] actor_base_t can be shutted down properly even if it did not started yet

0.08 (12-Apr-2020)

  • [bugfix] message's arguments are more correctly forwarded
  • [bugfix] actor's arguments are more correctly forwarded in actor's creation inrotor::supervisor_t and rotor::asio::supervisor_asio_t
  • [bugfix] rotor::asio::forwarder_t now more correctly dispatches boost::asio events to actor methods; e.g. it works correctly now with async_accept method of socket_acceptor

0.07 (02-Apr-2020)

  • [improvement] more modern cmake usage

0.06 (09-Nov-2019)

  • [improvement] registry actor was added to allow via name/address runtime matching do services discovery
  • [improvement, breaking] minor changes in supervisor behavior: now it is considered initialized when all its children confirmed initialization
  • [improvement] supervisor_policy_t was introduced to control supervisor behavior on a child-actor startup failure
  • [example] examples/ev/pong-registry.cpp how to use registry
  • [doc] patterns/Registry was added

0.05 (22-Sep-2019)

  • [improvement] response can be inherited from rotor::arc_base, to allow forwarding requests without copying it (i.e. just intrusive pointer is created)
  • [example] examples/boost-asio/beast-scrapper.cpp has been added; it demonstrates an app with pool of actor workers with request-response forwarding

0.04 (14-Sep-2019)

  • [improvement] the request-response approach is integrated to support basic reliable messaging: response notification failure will be delivered, if the expected response will not arrive in-time
  • [improvement] lambda subscribers are supported
  • [improvement] actor behavior has been introduced to offload actor's interface
  • [breaking] supervisor is constructed with help of supervisor_config_t, which contains shutdown timeout value
  • [breaking] supervisor does not spawns timeout timer for overall shutdown procedure, instead per-child timers are spawned. The root supervisor the same way monitors child-supervisor shut down
  • [breaking] supervisor create_actor method now takes child max init time value. If it does not confirm, the child actor will be asked for shut down.
  • [breaking] shutdown request sent to an child actor now timeout-tracked by supervisor. The message type has changed to message::shutdown_request_t
  • [breaking] init request sent to an child actor now timeout-tracked by supervisor. The message type has changed to message::init_request_t
  • [breaking] actor's state request message type now message::state_request_t, which follows the generic request/response pattern. The response type is now message::state_response_t.
  • [breaking] {asio, ev, ws} supervisor configs are renamed to have corresponding suffix.

0.03 (25-Aug-2019)

  • [improvement] locality notion was introduced, which led to possibility to build supervising trees, see blog-cpp-supervisors
  • [breaking] the outbound field in rotor::supervisor_t was renamed just to queue
  • [breaking] rotor::address_t now contains const void* locality
  • [breaking] rotor::asio::supervisor_config_t now contains std::shared_ptr to strand, instead of creating private strand for each supervisor
  • [bugfix] redundant do_start() method in rotor::supervisor_t was removed, since supervisor now is able to start self after completing initialization.
  • [bugfix] rotor::supervisor_t sends initialize_actor_t to self to advance own state to INITIALIZED via common actor mechanism, instead of changing state directly on early initialization phase (do_initialize)
  • [bugfix] rotor::asio::forwarder_t now more correctly dispatches boost::asio events to actor methods
  • [bugfix] rotor::ev::supervisor_ev_t properly handles refcounter

0.02 (04-Aug-2019)

  • Added libev support

0.01 (24-Jul-2019)

  • Initial version

cpp-rotor's People

Contributors

basiliscos avatar dfreese avatar j-roux avatar ltdjorge avatar melpon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cpp-rotor's Issues

message processing order for actors on the same locality

Currently the order of messaging is preserved, i.e. when message m1 follows m2, they will be processed by an actor as m1, then m2. This is correct, and what is expected.

However, them messages are sent to the same address, where actors (a1 and a2), belong to supervisors but the same locality, are subscribed, they are processed in the following order (we assume that a1 subscribed "earlier" then a2):

a1 processes m1, then m2 , and then
a2 processes m1 and then m2

Generally speaking, this is correct, however this is not what is expected: using the same locality, means, that actors use the same thread (or same context), and can access to shared resources without synchronization. So, the expected order of processing should be:

a1 processes m1
a2 processes m1
a1 processes m2
a2 processes m2

per actor policy

The various actor-spawning pieces are scattered across API:

  1. init-timeout is provided in create_actor
  2. shutdown-timeout is configure in per-actor
  3. init-failure and shutdown-failure triggers are hard-coded in supervisor.

The idea is to have an per-actor dedicated class (transferred per actor constructor) with the appropriate settings:

  • initi-timeout
  • shutdown-timeout
  • init-attempts
  • init-failure-behaviour: shutdown, crash, custom
  • shutdown-attempts
  • shutdown-failure-behaviour: crash, custom

CMake via ExternalProject_Add: No rule to make target 'install'

In my CMakeLists.txt

include( ExternalProject)
ExternalProject_Add(
    rotor
    GIT_REPOSITORY https://github.com/basiliscos/cpp-rotor.git
    GIT_TAG master
    PREFIX rotor
    CMAKE_ARGS
        -DBUILD_BOOST_ASIO=on
    CMAKE_CACHE_ARGS
        -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/rotor
)
$ cmake --build build
[ 10%] Performing update step for 'rotor'
From https://github.com/basiliscos/cpp-rotor
   7b23b99..925ade4  local-connect -> origin/local-connect
Current branch master is up to date.
Already up to date!
[ 20%] Performing configure step for 'rotor'
loading initial cache file /mnt/c/software/gitlab/raftlib/build/rotor/tmp/rotor-cache-Debug.cmake
-- Boost  found.
-- Found Boost components:
   date_time;system;regex
-- Configuring done
-- Generating done
-- Build files have been written to: /mnt/c/software/gitlab/raftlib/build/rotor/src/rotor-build
[ 30%] Performing build step for 'rotor'
Scanning dependencies of target rotor
[  9%] Building CXX object CMakeFiles/rotor.dir/src/rotor/actor_base.cpp.o
[ 18%] Building CXX object CMakeFiles/rotor.dir/src/rotor/address_mapping.cpp.o
[ 27%] Building CXX object CMakeFiles/rotor.dir/src/rotor/behavior.cpp.o
[ 36%] Building CXX object CMakeFiles/rotor.dir/src/rotor/error_code.cpp.o
[ 45%] Building CXX object CMakeFiles/rotor.dir/src/rotor/message.cpp.o
[ 54%] Building CXX object CMakeFiles/rotor.dir/src/rotor/subscription.cpp.o
[ 63%] Building CXX object CMakeFiles/rotor.dir/src/rotor/supervisor.cpp.o
[ 72%] Building CXX object CMakeFiles/rotor.dir/src/rotor/system_context.cpp.o
[ 81%] Linking CXX static library librotor.a
[ 81%] Built target rotor
Scanning dependencies of target rotor_asio
[ 90%] Building CXX object CMakeFiles/rotor_asio.dir/src/rotor/asio/supervisor_asio.cpp.o
[100%] Linking CXX static library librotor_asio.a
[100%] Built target rotor_asio
[ 40%] Performing install step for 'rotor'
make[3]: *** No rule to make target 'install'.  Stop.
CMakeFiles/rotor.dir/build.make:73: recipe for target 'rotor/src/rotor-stamp/rotor-install' failed
make[2]: *** [rotor/src/rotor-stamp/rotor-install] Error 2
CMakeFiles/Makefile2:72: recipe for target 'CMakeFiles/rotor.dir/all' failed
make[1]: *** [CMakeFiles/rotor.dir/all] Error 2
Makefile:83: recipe for target 'all' failed
make: *** [all] Error 2

introduce erlang-like child actor restarting

The API should be like:

using factory_t = std::function<r::actor_ptr_t(supervisor&)>;

enum class restart_policy_t { 
 always,
 never,
 transient // default
};

supervisor->lead(factory_t&&)
   .restart(restart_policy_t)
   .period(pt::milliseconds{500})      // 1 restart attempt per 500ms
   .spawn() // spawn actor item now

by default supervisor starts spawning all herd, when it started, i.e. it is not affected by child death.

Refs:

https://www.erlang.org/doc/design_principles/sup_princ.html#tuning-the-intensity-and-period

https://www.erlang.org/doc/design_principles/sup_princ.html

allow discover aliased services (registry_plugin)

When trying to link to an aliased service (i.e. the same address is exposed via different names) multiple times (due to transparency of aliased names), assert triggers:

rotor/src/rotor/plugin/link_client.cpp:52: virtual void rotor::plugin::link_client_plugin_t::link(const address_ptr_t&, bool, const link_callback_t&): Assertion `servers_map.count(address) == 0' failed.

The work around to create additional addresses in the service, and alias self via different addresses, but this is not handy.

Does rotor support http messaging between actors?

Can we scale programs using rotor across multiple servers and have actors able to find and communicate with each other? This is similar functionality to other actor frameworks, such as Orleans.

dependencies between actros

if actor1 depends on actor2, actor2 should stay alive to allow actor1 operate correctly.

If actor2 is asked for shutting down, it should ask it's dependencies first (actor1)? Or it should just notify them that it is going down?

It seems that shutdown request should propagate down to the leafs in graph of actors, and let the leafs be shut down first?

Should the cycles in dependencies between actors be determined and notified, then? As the shutdown procedure seems impossible

Remove calls to base::function() in overridden methods

Looking at the interface for actors, it would be nice to remove the noise from the overridden methods, so that the code only deals with its own logic. It's cleaner that way.

Instead of this:

void on_start() noexcept override {
    rotor::actor_base_t::on_start();
    ...
}

Have this, to eliminate the need to call rotor::actor_base_t::on_start():

class actor_base {
    void base_on_start() {
        // do base processing
        // ...
       on_start(); // Do overriding stuff.
    }

    virtual void on_start() {}
};

address contains non-owning pointer to supervisor

as address contains non-owning pointer to a supervisor it might be case when the supervisor is shutdown and destroyed, and a message is sent to it => crash.

On the other hand, the client/server relationship between actors should be set up, and only after that the communication should be safe/reliable. In case of shutdown, "server" should notify "clients" about the shutdown and wait clients for "unsubscription" of the services, and only after that continue shutdown.

request-responce match/tracking and processing

If an actor reacts on some message type (response), when actor sends an request we'd like to invoke specific to the response handler.

One obvious solution will be to include request message into response, and do some if in actor. Not very nice.

Another solution is to generate an new address, let the actor subscribes to the address, and let the actor sets this ephemeral address as reply_to in the request. That works, however what about fail-case? Also if there is an undefined number of such req/res, how to manage them?

Some pattern should be developed in rotor::core

introduce shutdown reason + more debugging

Sometimes it seems unclear why hierarchy is collapsing. It is obvious, that something happen, but not clear what is the root reason. So, when something go wrong, the shutdown message should setup error_code for easier examination of a supervisor.

In addition, when actor starts initialization, in the debug mode, it should print it's address and it's type. When it is going to down, it should also print it's type, address and the shutdown reason.

Seems not so complex to do.

introduce subsupervisor?

rationale: root supervisor provides execution context, meanwhile subsupervisor might inherit / share execution context with parent supervisor providing it's own supervising facilities.

For example: root EV-supervisor holds belongs to some thread and owns some loop. Subsupervisor might use the same thread/loop. In particular, messages to root supervisor will be delivered in synchronized manner, while for messages for sub-supervisors will be delivered the same manner as to regular actors.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.