Coder Social home page Coder Social logo

appbase's Introduction

AppBase

The AppBase library provides a basic framework for building applications from a set of plugins. AppBase manages the plugin life-cycle and ensures that all plugins are configured, initialized, started, and shutdown in the proper order.

Key Features

  • Dynamically Specify Plugins to Load
  • Automatically Load Dependent Plugins in Order
  • Plugins can specify commandline arguments and configuration file options
  • Program gracefully exits from SIGINT, SIGTERM, and SIGPIPE
  • Minimal Dependencies (Boost 1.60, c++14)

Defining a Plugin

A simple example of a 2-plugin application can be found in the /examples directory. Each plugin has a simple life cycle:

  1. Initialize - parse configuration file options
  2. Startup - start executing, using configuration file options
  3. Shutdown - stop everything and free all resources

All plugins complete the Initialize step before any plugin enters the Startup step. Any dependent plugin specified by APPBASE_PLUGIN_REQUIRES will be Initialized or Started prior to the plugin being Initialized or Started.

Shutdown is called in the reverse order of Startup.

class net_plugin : public appbase::plugin<net_plugin>
{
   public:
     net_plugin(){};
     ~net_plugin(){};

     APPBASE_PLUGIN_REQUIRES( (chain_plugin) );

     virtual void set_program_options( options_description& cli, options_description& cfg ) override
     {
        cfg.add_options()
              ("listen-endpoint", bpo::value<string>()->default_value( "127.0.0.1:9876" ), "The local IP address and port to listen for incoming connections.")
              ("remote-endpoint", bpo::value< vector<string> >()->composing(), "The IP address and port of a remote peer to sync with.")
              ("public-endpoint", bpo::value<string>()->default_value( "0.0.0.0:9876" ), "The public IP address and port that should be advertized to peers.")
              ;
     }

     void plugin_initialize( const variables_map& options ) { std::cout << "initialize net plugin\n"; }
     void plugin_startup()  { std::cout << "starting net plugin \n"; }
     void plugin_shutdown() { std::cout << "shutdown net plugin \n"; }

};

int main( int argc, char** argv ) {
   try {
      appbase::app().register_plugin<net_plugin>(); // implict registration of chain_plugin dependency
      if( !appbase::app().initialize( argc, argv ) )
         return -1;
      appbase::app().startup();
      appbase::app().exec();
   } catch ( const boost::exception& e ) {
      std::cerr << boost::diagnostic_information(e) << "\n";
   } catch ( const std::exception& e ) {
      std::cerr << e.what() << "\n";
   } catch ( ... ) {
      std::cerr << "unknown exception\n";
   }
   std::cout << "exited cleanly\n";
   return 0;
}

This example can be used like follows:

./examples/appbase_example --plugin net_plugin
initialize chain plugin
initialize net plugin
starting chain plugin
starting net plugin
^C
shutdown net plugin
shutdown chain plugin
exited cleanly

Boost ASIO

AppBase maintains a singleton application instance which can be accessed via appbase::app(). This application owns a boost::asio::io_service which starts running when appbase::exec() is called. If a plugin needs to perform IO or other asynchronous operations then it should dispatch it via application io_service which is setup to use an execution priority queue.

app().post( appbase::priority::low, lambda )

OR

delay_timer->async_wait( app().get_priority_queue().wrap( priority::low, lambda ) );

Use of get_io_service() directly is not recommended as the priority queue will not be respected.

Because the app calls io_service::run() from within application::exec() and does not spawn any threads all asynchronous operations posted to the io_service should be run in the same thread.

Graceful Exit

To trigger a graceful exit call appbase::app().quit() or send SIGTERM, SIGINT, or SIGPIPE to the process.

Dependencies

  1. c++14 or newer (clang or g++)
  2. Boost 1.60 or newer compiled with C++14 support

To compile boost with c++14 use:

./b2 ...  cxxflags="-std=c++0x -stdlib=libc++" linkflags="-stdlib=libc++" ...

appbase's People

Contributors

b1bart avatar bytemaster avatar heifner avatar jgiszczak avatar larryk85 avatar mitza-oci avatar nathanielhourt avatar spartucus avatar spoonincode avatar swatanabe-b1 avatar tbfleming avatar wanderingbort 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

Watchers

 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

appbase's Issues

examples/appbase_example cannot link, missing pthread

When trying to compile appbase as a standalone library, it failed until I added pthread to examples/CMakeLists.txt:

add_executable( appbase_example main.cpp )
target_link_libraries( appbase_example appbase ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} pthread)

include config files from config files

It would be nice for a config file to have the ability to include another config file, maybe with a include=nodes.ini or something. This would allow for easier sharing of config items across multiple nodes (think blacklists, peerlists, etc)

Incompatible with Boost 1.71

Boost 1.71 is the one delivered in Ubuntu 20.04 libraries. Appbase fails to compile with it. What is the midterm planin supporting Boost?

truncated output:

[ 62%] Building CXX object CMakeFiles/chronicle-receiver.dir/chronicle-receiver/receiver_plugin.cpp.o
In file included from /usr/include/boost/asio/executor.hpp:338,
                 from /usr/include/boost/asio/basic_socket.hpp:27,
                 from /usr/include/boost/asio/basic_datagram_socket.hpp:20,
                 from /usr/include/boost/asio.hpp:24,
                 from /opt/src/eos-chronicle/external/appbase/include/appbase/channel.hpp:7,
                 from /opt/src/eos-chronicle/external/appbase/include/appbase/application.hpp:3,
                 from /opt/src/eos-chronicle/chronicle-receiver/receiver_plugin.hpp:1,
                 from /opt/src/eos-chronicle/chronicle-receiver/receiver_plugin.cpp:3:
/usr/include/boost/asio/impl/executor.hpp: In instantiation of ‘boost::asio::execution_context& boost::asio::executor::impl< <template-parameter-1-1>, <template-parameter-1-2> >::context() [with Executor = std::reference_wrapper<boost::asio::io_context>; Allocator = std::allocator<void>]’:
/usr/include/boost/asio/impl/executor.hpp:177:22:   required from here
/usr/include/boost/asio/impl/executor.hpp:179:22: error: ‘class std::reference_wrapper<boost::asio::io_context>’ has no member named ‘context’
  179 |     return executor_.context();
      |            ~~~~~~~~~~^~~~~~~
/usr/include/boost/asio/impl/executor.hpp: In instantiation of ‘void boost::asio::executor::impl< <template-parameter-1-1>, <template-parameter-1-2> >::on_work_started() [with Executor = std::reference_wrapper<boost::asio::io_context>; Allocator = std::allocator<void>]’:
/usr/include/boost/asio/impl/executor.hpp:167:8:   required from here
/usr/include/boost/asio/impl/executor.hpp:169:15: error: ‘class std::reference_wrapper<boost::asio::io_context>’ has no member named ‘on_work_started’
  169 |     executor_.on_work_started();
      |     ~~~~~~~~~~^~~~~~~~~~~~~~~
/usr/include/boost/asio/impl/executor.hpp: In instantiation of ‘void boost::asio::executor::impl< <template-parameter-1-1>, <template-parameter-1-2> >::on_work_finished() [with Executor = std::reference_wrapper<boost::asio::io_context>; Allocator = std::allocator<void>]’:
/usr/include/boost/asio/impl/executor.hpp:172:8:   required from here
/usr/include/boost/asio/impl/executor.hpp:174:15: error: ‘class std::reference_wrapper<boost::asio::io_context>’ has no member named ‘on_work_finished’
  174 |     executor_.on_work_finished();

application::shutdown() does not empty the execution queue

If the application needs to stop (in my case, the consumer closes connection, and I need to stop processing of state history) and calls app().shutdown();, the priority queue still has entries pending execution. So, application::exec() continues running through the queue, but io_serv is already reset.

As a result, sisgegv is thrown, because io_serv is NULL by that time.

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.