3 Star 4 Fork 1

Gitee 极速下载/REACT-CPP

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
此仓库是为了提升国内下载速度的镜像仓库,每日同步一次。 原始仓库: https://github.com/CopernicaMarketingSoftware/REACT-CPP
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
Apache-2.0

REACT-CPP

REACT-CPP is an event loop library that utilizes the new C++11 lambda functions to notify you when there is activity on a filedescriptor or on a timer. Internally, it is a wrapper around the libev library, and does therefore also depend on that library.

ABOUT

The REACT-CPP library is created and maintained by Copernica (www.copernica.com). Do you appreciate our work and are you looking for other high quality solutions? Then check out our other solutions:

  • PHP-CPP (www.php-cpp.com)
  • PHP-JS (www.php-js.com)
  • Copernica Marketing Suite (www.copernica.com)
  • MailerQ MTA (www.mailerq.com)
  • Responsive Email web service (www.responsiveemail.com)

EVENT LOOP

The React::Loop and the React::MainLoop classes are the central classes of this library. These classes have methods to set timers and to register callback functions that will be called when a filedescriptor becomes readable or writable.

In a typical application you create an instance of the mainloop class, and then you register filedescriptors that you'd like to watch for readability, register event handlers and timers:

#include <reactcpp.h>
#include <unistd.h>
#include <iostream>

/**
 *  Main application procedure
 *  @return int
 */
int main()
{
    // create an event loop
    React::MainLoop loop;

    // set a timer to stop the application after five seconds
    loop.onTimeout(5.0, []() {
    
        // report that the timer expired
        std::cout << "timer expired" << std::endl;
    
        // stop the application
        exit(0);
    });
    
    // we'd like to be notified when input is available on stdin
    loop.onReadable(STDIN_FILENO, []() -> bool {
    
        // read input
        std::string buffer;
        std::cin >> buffer;
    
        // show what we read
        std::cout << buffer << std::endl;
        
        // return true, so that we also return future read events
        return true;
    });

    // handler when control+c is pressed
    loop.onSignal(SIGINT, []() -> bool {
        
        // report that we got a signal
        std::cout << "control+c detected" << std::endl;
        
        // stop the application
        exit(0);
        
        // although this code is unreachable, we return false because
        // we're no longer interested in future SIGINT signals
        return false;
    });

    // run the event loop
    loop.run();

    // done
    return 0;
}

The above example contains a very simple echo application. Everything that the application reads from stdin is directly echo'd back to stdout. After five seconds the application automatically stops, and when the SIGINT signal is caught, the application also exits.

There is a subtle difference between the React::MainLoop that we use in the example above, and the React::Loop class that is also available. The React::MainLoop is supposed to run the main event loop for the entire application, while the React::Loop classes are additional event loops that you can (for example) use in additional threads. In normal circumstances, you will never have to instantiate more than once instance of the React::MainLoop class, while it is perfectly legal to create as many React::Loop objects as you wish.

Because the React::MainLoop class is intended to control the entire application, it has some additional methods to register signal handlers and handlers to monitor child processes. Such methods are not available in the regular React::Loop class.

CALLBACK RETURN VALUES

In the first example we showed how to install handlers on the loop object. Once such a handler is set, the loop will keep calling it every time a filedescriptor becomes active. But what if you no longer are interested in these events? In that case you have a number of options to stop a callback from being called.

The first one is by having your callback function return false. If your callback returns a boolean false value, your handler function is removed from the event loop and you will no longer be notified. If you return true on the other hand, the handler will stay in the event loop, and will also be called in the future.

#include <reactcpp.h>
#include <unistd.h>
#include <iostream>

int main()
{
    // create the event loop
    React::MainLoop loop;
    
    // we'd like to be notified when input is available on stdin
    loop.onReadable(STDIN_FILENO, []() -> bool {
    
        // read input
        std::string buffer;
        std::cin >> buffer;
    
        // show what we read
        std::cout << buffer << std::endl;
        
        // from this moment on, we no longer want to receive updates
        return false;
    });
    
    // run the event loop
    loop.run();
    
    // done
    return 0;
}

The program above is only interested in read events until the first line from stdin is read. After that it returns false, to inform the event loop that it is no longer interested in read events.

This also means that the program in the example automatically exits after the first line. The reason for this is that the run() method of the React::Loop and React::MainLoop classes automatically stops running when there are no more callback functions active. By returning false, the last and only registered callback function is cancelled, and the event loop has nothing left to monitor.

RETURN VALUE OF LOOP METHODS

The Loop::onReadable(), Loop::onWritable(), etcetera methods all return a (shared) pointer to a watcher object. In the first example we had not used this return value, but you can store this watcher object in a variable. If you have access to this watcher object, you can cancel calls to your handler without having to wait for your callback to be called first.

The returned watcher is a shared_ptr. Internally, the library also keeps a pointer to this object, so that even if you decide to discard the watcher object, it will live on inside the lib. The only way to stop the callback from being active is either by calling the cancel() method on the watcher object, or by having your callback function return false.

With this knowledge we are going to modify our earlier example. The echo application that we showed before is updated to set the timer back to five seconds every time that some input is read. The application will therefore no longer stop five seconds after it was started, but five seconds after the last input was received. We also change the signal watcher: the moment CTRL+C is pressed, the application stops responding, and will delay it's exit for one second.

The watcher objects all have in common that they have a cancel() method that stops further events from being delivered to your callback function. Next to the cancel() method, additional methods are available to deal with the specific behavior of the item being watched.

#include <reactcpp.h>
#include <unistd.h>
#include <iostream>

/**
 *  Main application procedure
 *  @return int
 */
int main()
{
    // create an event loop
    React::MainLoop loop;

    // set a timer to stop the application if it is idle for five seconds
    // note that the type of 'timer' is std::shared_ptr<React::TimeoutWatcher>,
    // also note that the timer callback does not return a boolean, as a timer
    // stops anyway after it expires.
    auto timer = loop.onTimeout(5.0, []() {
    
        // report that the timer expired
        std::cout << "timer expired" << std::endl;
    
        // stop the application
        exit(0);
    });
    
    // we'd like to be notified when input is available on stdin
    // the type of 'reader' is std::shared_ptr<React::ReadWatcher>
    auto reader = loop.onReadable(STDIN_FILENO, [timer]() -> bool {
    
        // read input
        std::string buffer;
        std::cin >> buffer;
    
        // show what we read
        std::cout << buffer << std::endl;
        
        // set the timer back to five seconds
        timer->set(5.0);
        
        // keep checking for readability
        return true;
    });

    // handler when control+c is pressed
    loop.onSignal(SIGINT, [&loop, timer, reader]() -> bool {
        
        // report that we got a signal
        std::cout << "control+c detected" << std::endl;
        
        // both the timer, and the input checker can be cancelled now
        timer->cancel();
        reader->cancel();
        
        // stop the application in one second
        loop.onTimeout(1.0, []() {
        
            // exit the application
            exit(0);
        });
        
        // no longer check for signals
        return false;
    });

    // run the event loop
    loop.run();

    // done
    return 0;
}

CONSTRUCT WATCHER OBJECTS

Up to now we had registered callback methods via the Loop::onSomething() methods. These methods return a shared pointer to an object that keeps the watcher state. It is also possible to create such objects directly, without calling a Loop::onSomething method(). This can be very convenient, because you will have ownership of the object (instead of the event loop) and you can unregister your handler function by just destructing the object.

#include <reactcpp.h>
#include <unistd.h>
#include <iostream>

/**
 *  Main application procedure
 *  @return int
 */
int main()
{
    // create an event loop
    React::MainLoop loop;

    // we'd like to be notified when input is available on stdin
    React::ReadWatcher reader(loop, STDIN_FILENO, []() -> bool {
    
        // read input
        std::string buffer;
        std::cin >> buffer;
    
        // show what we read
        std::cout << buffer << std::endl;
        
        // keep checking readability
        return true;
    });

    // run the event loop
    loop.run();

    // done
    return 0;
}

Conceptually, there is not a big difference between calling Loop::onReadable() to register a callback function, or by instantiating a React::ReadWatcher object yourself. In my opinion, the code that utilizes a call to Loop::onReadable() is easier to understand and maintain, but by creating a ReadWatcher class yourself, you have full ownership of the class and can destruct it whenever you like - which can be useful too.

FILEDESCRIPTORS

Filedescriptors can be checked for activity by registering callbacks for readability and writability. The loop object has two methods for that:

std::shared_ptr<ReadWatcher> Loop::onReadable(int fd, const ReadCallback &callback);
std::shared_ptr<WriteWatcher> Loop::onWritable(int fd, const WriteCallback &callback);

The callbacks are simple functions that return a bool, and that do not take any parameters. If they return true, the filedescriptor will stay in the event loop and your callback will also be called in the future if the filedescriptor becomes readable or writable again. If the function returns false, the descriptor is removed from the event loop.

You can also create a ReadWatcher or WriteWatcher object yourself. In that case you will not have to use the Loop::onReadable() or Loop::onWritable() methods:

ReadWatcher watcher(&loop, fd, []() -> bool { ...; return true; });
WriteWatcher watcher(&loop, fd, []() -> bool { ...; return true; });

TIMERS AND INTERVALS

The React library supports both intervals and timers. A timer is triggered only once, an interval on the other hand calls the registered callback method every time the interval time has expired.

When you create an interval, you can specify both the initial expire time as well as the interval between all subsequent calls. If you omit the initial time, the callback will be first called after the first interval has passed.

std::shared_ptr<TimeoutWatcher> 
Loop::onTimeout(Timestamp seconds, const TimeoutCallback &callback);

std::shared_ptr<IntervalWatcher> 
Loop::onInterval(Timestamp interval, const IntervalCallback &callback);

std::shared_ptr<IntervalWatcher> 
Loop::onInterval(Timestamp initial, Timestamp interval, const IntervalCallback &callback);

Just like the callbacks for filedescriptors, the callback for intervals should return a boolean value to indicate whether the interval timer should be kept in the event loop or not. The callback function for timeouts does not return any value, because timeouts only trigger once, and are never kept in the event loop.

loop.onTimeout(3.0, []() { ... });
loop.onInterval(5.0, []() -> bool { ...; return true; });
loop.onInterval(0.0, 5.0, []() -> bool { ...; return true; });

And you can of course also instantiate React::TimeoutWatcher and React::IntervalWatcher objects directly:

TimeoutWatcher watcher(&loop, 3.0, []() { ... });
IntervalWatcher watcher(&loop, 5.0, []() -> bool { ...; return true; });
IntervalWatcher watcher(&loop, 2.0, 5.0, []() -> bool { ...; return true; });

SIGNALS

Signals can be watched too. Normally, signals are delivered to your application in an asynchronous way, and the signal handling code could be started when your application is in the middle of running some other algorithm. By registering a signal handler via the React::MainLoop class, you can prevent this, and have your signal handling be executed as part of the event loop.

Setting up a signal handler is just as easy as setting up callback functions for filedescriptors or timers. The loop object has a simple onSignal() method for it:

std::shared_ptr<SignalWatcher> MainLoop::onSignal(int signum, const SignalCallback &callback);

And the callback function (of course) should return a boolean value to tell the event loop if the handler should be kept in the loop or not.

loop.onSignal(SIGTERM, []() -> bool { ...; return true; });

And for signals it also is possible to bypass the methods on the loop class, and create a React::Signal object yourself:

SIgnalWatcher watcher(&loop, SIGTERM, []() -> bool { ...; return true; });

CHILD PROCESSES

The MainLoop class also allows you to watch for status change events from child processes. This is useful if your application forks off child processes, and wants to be notified when one of these child processes changes its status (like exiting).

Both the pid and trace parameters are optional. If you do not specify a pid (or set the parameter to zero), your callback function will be called for every single child process that changes its status. The boolean trace parameter can be used to indicate whether you'd like to be notified for every possible status change (including changes between paused and running state), or only when child processes terminate. Set trace to true to receive all notifications, and to false to receive only for process exits.

std::shared_ptr<StatusWatcher> 
MainLoop::onStatusChange(pid_t pid, bool trace, const SignalCallback &callback);

The callback function has a different signature than most of the other callbacks, as it should accept two parameters: the pid of the process for which the status changed, and its new status. The return value should be true if you want to keep the child watcher active, or false if you no longer want to be informed about child process status changes.

loop.onStatusChange(pid, false, [](pid_t pid, int status) -> bool { ...; return true; });

And just like all other watcher objects, you can also create StatusWatcher objects yourself:

StatusWatcher watcher(&loop, pid, trace, [](pid_t pid, int status) -> bool { ... });

THREAD SYNCHRONIZATION

Let's introduce a topic that has not been addressed in one of the examples: running multiple threads and optionally multiple thread loops.

If your application runs multiple threads, there is a pretty good chance that sooner or later you want to get these threads in sync. When you have, for example, a worker thread that wants to report its results back to the main thread, it should somehow notify that thread that the result of the calculatations are somewhere to be picked up. If the main thread is busy running an event loop, it should be able to interupt that event loop, so that the data can be picked up. This all can be done with the Loop::onSynchronize() method.

#include <reactcpp.h>
#include <unistd.h>
#include <iostream>
#include <thread>

/**
 *  Main procedure
 */
int main()
{
    // create a thread loop
    React::MainLoop loop;
    
    // install a callback that can be called by a worker thread.
    // the returned synchronizer object is of type std::shared_ptr<SynchronizeWatcher>, 
    // and contains a thread safe object that can be accessed from other threads 
    // to notify us
    auto synchronizer = loop.onSynchronize([]() {
        std::cout << "other thread has finished running" << std::endl;
    });
    
    // start a new thread
    std::thread thread([synchronizer]() {
    
        // some long running algorithm
        sleep(1);
        
        // notify the main event loop that the task was completed
        synchronizer->synchronize();
    });
    
    // we'd like to be notified when input is available on stdin
    loop.onReadable(STDIN_FILENO, []() -> bool {
    
        // read input
        std::string buffer;
        std::cin >> buffer;
    
        // show what we read
        std::cout << buffer << std::endl;
        
        // keep receiving readability notifications
        return true;
    });

    // run the event loop
    loop.run();
    
    // join the thread
    thread.join();
    
    // done
    return 0;
}

The example above demonstrates how threads can synchronize with each other. First, you create an endpoint that the other thread can use to call the main thread, and you install a handler that will be called when the other thread uses that endpoint. Both steps are taken by a simple call to Loop::onSynchronize(). This installs the callback function that runs in the main thread, and it returns the thread safe endpoint that can be used by other thread to interup the main event loop.

The SynchronizeWatcher is similar to classes like React::ReadWatcher, React::WriteWatcher, React::TimeoutWatcher, etcetera. However, the callback is slightly different as it does not return a value. The watcher is active for as long as you have a reference to the synchronizer object.

// example how to install a synchronizer via the Loop class
auto watcher = loop.onSynchronize([]() { ... });

// example how to install the synchronizer as an object
SynchronizeWatcher watcher(loop, []() { ... });

When you use this technology to synchronize threads, you probably need to have some shared data. You could for example use a queue that is accessible by both threads. The worker thread pushes results to it, then calls synchronizer->synchronnize() to notify the master thread, which can then pick up the results from the queue. If the queue object is not thread safe, you must make sure that you protect access to it, for example by using mutex variables.

Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

简介

REACT-CPP 是利用 C++ 11 的 lambda 表达式在文件描述符或定时器激活的时候进行通知的事件循环库 展开 收起
README
Apache-2.0
取消

发行版

暂无发行版

贡献者

全部

近期动态

不能加载更多了
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C/C++
1
https://gitee.com/mirrors/REACT-CPP.git
git@gitee.com:mirrors/REACT-CPP.git
mirrors
REACT-CPP
REACT-CPP
master

搜索帮助