Cross-platform underlying library https://github.com/hujianzhe/util, download and place it in the BootServer directory
Introduction:
The code only implements service node startup bootstrapping, task scheduling, and basic module description. It does not contain any business code. It is implemented in pure C. It is generally compiled into a dynamic library and remains extremely restrained in terms of usage functions. It implements some common common protocol flows and supports C++. 20 Extension of stackless coroutine and isolated from some codes of pure C dynamic library to prevent C++ from contaminating the framework layer and affecting the generation of dynamic library
The code in util is responsible for cross-platform, except it does not require the installation of any third-party libraries
Operation process introduction:
The business process calls the dynamic library compiled by the code and calls the corresponding interface for use. For details on the calling process, see the test node example (main_template in the BootServer directory is a set of process-based startup code templates)
Multiple threads handle network IO reading and writing inside the module. A separate accept access thread will initially open a worker thread to process internal messages and received network messages and dispatch them to your business code logic. The worker thread uses a stack coroutine for scheduling processing (see below for the reason why a stackless coroutine is not used)
Worker threads use stack coroutines for scheduling by default to maintain the highest compatibility. You can also easily use C++20 stackless coroutines (one implemented in the util library) for scheduling. There are stack coroutines and stackless coroutines. processes can coexist
Module introduction and sample code:
1. BootServer: main code part, necessary initialization and operation of service nodes
2. ServiceTemplate: Service node code template, used to write your business logic
3. SoTestClient, SoTestServer: test nodes and wrote some sample codes
4. Cpp20-SoTestServer: test node, wrote some sample code, enabled C++20 stackless coroutine in main.cpp (using a C++20 stackless coroutine scheduler in the util library)
Compile:
windows direct VS compilation
Use make debug / make release / make asan under linux/Mac OS X
start up:
Edit the configuration file required for service node startup (see the attached configuration file template for the specific format), and give each node a configuration file and unique id, log identification name, IP and port number.
Windows opens directly in VS, and the project is configured with startup parameters <configuration file>
After linux/mac is compiled, sh run.sh <service process> <configuration file>
Some design reasons and insights: Q: Why not use stackless coroutines but use stacked coroutines? A: It is easy to implement stackless coroutines in pure C (for detailed code, you can see the stackless coroutine scheduler implemented in pure C in the util library), but resource recycling and persistence (especially variables on the stack are subject to coroutine re-entry). (the latter situation) is extremely difficult If you want to use stackless coroutines smoothly, you still need to rely on compiler support. This has been achieved in C++20. There is also a complete C++20 stackless coroutine scheduler implementation in the util library.
Q: Why does the stackless coroutine provide this extended function in the form of a header file?
A: 1. Because stackless coroutines are code intrusive and will change a large number of function signature forms
2. Contains C++ objects, which are not recognized in C, and the dynamic library cannot be successfully exported.
3. Given in the form of a header file, handing over the activation permission of stackless coroutines to the application layer is the way I currently think of to deal with the pure C dynamic library part without any pollution.
Q: Can it be replaced with other schedulers?
A: The scheduler of the worker thread can be replaced. The worker thread is designed as the running carrier of the scheduler. The interaction between the network thread and the worker thread within the framework can correspond to the scheduling behavior through the interface hook.
Q: Can it be replaced with other network libraries?
A: 1. It cannot be replaced with other network libraries at present, but this issue has been considered at the beginning of the design. The network part and the task scheduling part are completely separated. In the future, corresponding behaviors similar to worker thread hooks will be provided for replacement.
2. Although there are many third-party network libraries, their focuses are different. There are TCP and UDP libraries used for application development, there are also libraries that wish to include the entire universe, and there are also libraries that implement the entire network protocol stack at the application layer, so in fact this area is not unified.
3. If a set of network libraries enters the standard in the future, then I will replace it.
Q: Why not just use C++ as the framework?
A: 1. When this set of code was written, C++20 had not yet appeared. At that time, the relatively most mature coroutine solution was stack coroutine. This could be done using C by calling the corresponding platform system API.
2. The functions to be implemented by this type of framework have been solidified, and the life cycle of resources is solidified by the process. It is just enough to implement it in pure C (there was a version implemented in C++ before, and the code was more complicated)
3. If the dynamic library is called by other modules, it still needs to seal an interface in C
4.C++ exported classes are contagious, and the ABI is not unified
Q: Can the business layer continue to be developed using pure C?
A: It is highly not recommended, because the writing of asynchronous processes and exceptions thrown in modules written in high-level languages make the timing of resource destruction uncertain. At this time, it is extremely difficult to manually control resources using pure C. You should use a higher-level language to handle these things. For example, you can use C++ to develop upper-level business code, and its RAII mechanism can ensure the release of corresponding resources.
Q: If I want to transform the "callback" of the old project into a "coroutine", can I just replace the scheduler part with the corresponding call point in the business code?
A: It’s not that simple. The first is the workload issue. In addition, whether it is in callback form or coroutine form, the essence is to issue a request and wait for the result. During this period, the life cycle of the variable is a problem that must be solved and needs to be carefully considered. If it is Raw pointer, then it can almost be said that it cannot be transformed if the project has used Means such as std::shared_ptr lengthen the life cycle of variables, so it will be less difficult to transform into a "stack coroutine" version. If you want to transform into a C++20 stackless coroutine, the workload is huge. It is tantamount to rewriting the project (because stackless coroutines have strong code intrusion), so it is recommended not to transform old projects.
Q: Why is coroutine currently not allowed to be migrated to different threads for execution?
A: Migrating coroutines can be easily done, but the reason why it is not provided is
1. The tasks executed between coroutines are uncertain, which may cause io and calculation to be mixed in the same scheduling thread.
2. After migration, the same coroutine process may run on different threads. In this case, you must ensure that your code does not rely on any thread local variables, but you cannot ensure that the third-party library does not use thread local variables.
Q: Will asan crash when compiling and running?
A: 1. If you are using the framework's default stacked coroutine, it is definitely not a code problem. You can adjust the stacked coroutine stack size in the node configuration file (when ASAN is enabled, a relatively large stack space will be consumed, so There is a possibility of stack explosion)
2. ASAN does not fully support the stacked coroutine API (ucontext) in the Unix environment. Although the ucontext coroutine API has been adapted to ASAN in the util code, it still cannot 100% guarantee that there will be no problems running in the ASAN environment. (so far so good)
3. In some newer Linux distributions, if ASAN outputs AddressSanitizer: DEADLYSIGNAL infinitely, adjust sudo sysctl vm.mmap_rnd_bits=28 to solve the problem.
TODO:
1. I really don’t have time to write a detailed documentation.
2. Provide support for writing business logic in scripting language