• Guest
HabraHabr
  • Main
  • Users

  • Development
    • Programming
    • Information Security
    • Website development
    • JavaScript
    • Game development
    • Open source
    • Developed for Android
    • Machine learning
    • Abnormal programming
    • Java
    • Python
    • Development of mobile applications
    • Analysis and design of systems
    • .NET
    • Mathematics
    • Algorithms
    • C#
    • System Programming
    • C++
    • C
    • Go
    • PHP
    • Reverse engineering
    • Assembler
    • Development under Linux
    • Big Data
    • Rust
    • Cryptography
    • Entertaining problems
    • Testing of IT systems
    • Testing Web Services
    • HTML
    • Programming microcontrollers
    • API
    • High performance
    • Developed for iOS
    • CSS
    • Industrial Programming
    • Development under Windows
    • Image processing
    • Compilers
    • FPGA
    • Professional literature
    • OpenStreetMap
    • Google Chrome
    • Data Mining
    • PostgreSQL
    • Development of robotics
    • Visualization of data
    • Angular
    • ReactJS
    • Search technologies
    • Debugging
    • Test mobile applications
    • Browsers
    • Designing and refactoring
    • IT Standards
    • Solidity
    • Node.JS
    • Git
    • LaTeX
    • SQL
    • Haskell
    • Unreal Engine
    • Unity3D
    • Development for the Internet of things
    • Functional Programming
    • Amazon Web Services
    • Google Cloud Platform
    • Development under AR and VR
    • Assembly systems
    • Version control systems
    • Kotlin
    • R
    • CAD/CAM
    • Customer Optimization
    • Development of communication systems
    • Microsoft Azure
    • Perfect code
    • Atlassian
    • Visual Studio
    • NoSQL
    • Yii
    • Mono и Moonlight
    • Parallel Programming
    • Asterisk
    • Yandex API
    • WordPress
    • Sports programming
    • Lua
    • Microsoft SQL Server
    • Payment systems
    • TypeScript
    • Scala
    • Google API
    • Development of data transmission systems
    • XML
    • Regular expressions
    • Development under Tizen
    • Swift
    • MySQL
    • Geoinformation services
    • Global Positioning Systems
    • Qt
    • Dart
    • Django
    • Development for Office 365
    • Erlang/OTP
    • GPGPU
    • Eclipse
    • Maps API
    • Testing games
    • Browser Extensions
    • 1C-Bitrix
    • Development under e-commerce
    • Xamarin
    • Xcode
    • Development under Windows Phone
    • Semantics
    • CMS
    • VueJS
    • GitHub
    • Open data
    • Sphinx
    • Ruby on Rails
    • Ruby
    • Symfony
    • Drupal
    • Messaging Systems
    • CTF
    • SaaS / S+S
    • SharePoint
    • jQuery
    • Puppet
    • Firefox
    • Elm
    • MODX
    • Billing systems
    • Graphical shells
    • Kodobred
    • MongoDB
    • SCADA
    • Hadoop
    • Gradle
    • Clojure
    • F#
    • CoffeeScript
    • Matlab
    • Phalcon
    • Development under Sailfish OS
    • Magento
    • Elixir/Phoenix
    • Microsoft Edge
    • Layout of letters
    • Development for OS X
    • Forth
    • Smalltalk
    • Julia
    • Laravel
    • WebGL
    • Meteor.JS
    • Firebird/Interbase
    • SQLite
    • D
    • Mesh-networks
    • I2P
    • Derby.js
    • Emacs
    • Development under Bada
    • Mercurial
    • UML Design
    • Objective C
    • Fortran
    • Cocoa
    • Cobol
    • Apache Flex
    • Action Script
    • Joomla
    • IIS
    • Twitter API
    • Vkontakte API
    • Facebook API
    • Microsoft Access
    • PDF
    • Prolog
    • GTK+
    • LabVIEW
    • Brainfuck
    • Cubrid
    • Canvas
    • Doctrine ORM
    • Google App Engine
    • Twisted
    • XSLT
    • TDD
    • Small Basic
    • Kohana
    • Development for Java ME
    • LiveStreet
    • MooTools
    • Adobe Flash
    • GreaseMonkey
    • INFOLUST
    • Groovy & Grails
    • Lisp
    • Delphi
    • Zend Framework
    • ExtJS / Sencha Library
    • Internet Explorer
    • CodeIgniter
    • Silverlight
    • Google Web Toolkit
    • CakePHP
    • Safari
    • Opera
    • Microformats
    • Ajax
    • VIM
  • Administration
    • System administration
    • IT Infrastructure
    • *nix
    • Network technologies
    • DevOps
    • Server Administration
    • Cloud computing
    • Configuring Linux
    • Wireless technologies
    • Virtualization
    • Hosting
    • Data storage
    • Decentralized networks
    • Database Administration
    • Data Warehousing
    • Communication standards
    • PowerShell
    • Backup
    • Cisco
    • Nginx
    • Antivirus protection
    • DNS
    • Server Optimization
    • Data recovery
    • Apache
    • Spam and antispam
    • Data Compression
    • SAN
    • IPv6
    • Fidonet
    • IPTV
    • Shells
    • Administering domain names
  • Design
    • Interfaces
    • Web design
    • Working with sound
    • Usability
    • Graphic design
    • Design Games
    • Mobile App Design
    • Working with 3D-graphics
    • Typography
    • Working with video
    • Work with vector graphics
    • Accessibility
    • Prototyping
    • CGI (graphics)
    • Computer Animation
    • Working with icons
  • Control
    • Careers in the IT industry
    • Project management
    • Development Management
    • Personnel Management
    • Product Management
    • Start-up development
    • Managing the community
    • Service Desk
    • GTD
    • IT Terminology
    • Agile
    • Business Models
    • Legislation and IT-business
    • Sales management
    • CRM-systems
    • Product localization
    • ECM / EDS
    • Freelance
    • Venture investments
    • ERP-systems
    • Help Desk Software
    • Media management
    • Patenting
    • E-commerce management
    • Creative Commons
  • Marketing
    • Conferences
    • Promotion of games
    • Internet Marketing
    • Search Engine Optimization
    • Web Analytics
    • Monetize Web services
    • Content marketing
    • Monetization of IT systems
    • Monetize mobile apps
    • Mobile App Analytics
    • Growth Hacking
    • Branding
    • Monetize Games
    • Display ads
    • Contextual advertising
    • Increase Conversion Rate
  • Sundry
    • Reading room
    • Educational process in IT
    • Research and forecasts in IT
    • Finance in IT
    • Hakatonas
    • IT emigration
    • Education abroad
    • Lumber room
    • I'm on my way

Asynchronous business logic these days

In short:
 
 
Proof has already been implemented on
C ++
,
JS
and
PHP
, suitable for
Java
.
 
Faster than
than coroutine and Promise, more features.
 
Does not require the allocation of a separate software stack.
 
It is friendly with all the security tools and debugging.
 
Works on any architecture and does not require special compiler flags.
 
 
Looking back
 
FutoIn AsyncSteps - an alternative to coroutines
 
To the numbers
 
 
Looking back
 
At the dawn of the computer, there was a single control flow with I /O blocking. Then, interruptions of iron were added to it. There was an opportunity to effectively use slow and unpredictable devices.
 
With the increase in the capacity of iron and its low availability, it became necessary to perform several tasks simultaneously, which provided hardware support. So there were isolated processes with abstracted from iron interrupts in the form of signals.
 
The next evolutionary stage was multithreading, which was implemented on the foundation of the same processes, but with shared access to memory and other resources. This approach has its limitations and significant overhead for switching in a secure OS.
 
For communication between processes and even different machines, the Promise /Future abstraction was proposed 40+ years ago.
 
User interfaces and the ridiculous 10K client problem have led to the heyday of Event Loop, Reactor and Proactor approaches that are more event-driven than clear, consistent business logic.
 
Finally came to the modern coroutine (coroutine), which are essentially emulated flows over the above-described abstractions with the appropriate technical limitations and deterministic control transfer.
 
To transfer events, results and exceptions, everything has returned to the same Promise /Future concept. Some offices have decided to name a little differently - "Task".
 
In the end, everything was hidden in a beautiful package async /await , which requires the support of a compiler or translator, depending on the technology.
 

Problems with current situations of asynchronous business logic


 

Consider only coroutines and Promise, decorated with async /await , t. The presence of problems in older approaches confirms the very process of evolution.


 

These two terms are not identical. For example, ECMAScript does not have coroutines, but there are syntactic simplifications for using Promise , which in turn only organizes work with the hell of callbacks (callback hell). In fact, script engines like V8 go further and make special optimizations for pure async /await functions and calls.


 

Sayings of experts about those not included in C ++ 17 co_async /co_await there are here on the resource , but the pressure of the software giant coroutines can still appear in the standard in their form. Meanwhile, the traditional recognized solution is Boost.Context , Boost.Fiber and Boost.Coroutine2 .


 

In Java, there is still no async /await at the level of the language, but there are solutions like EA Async , which, like Boost.Context, must be customized for each version of the JVM and byte code.


 

There are coroutines in Go, but if you look closely at articles and bug reports of open projects, it turns out that not everything is as smooth as that. Perhaps losing the coroutine interface as a managed entity is not the best idea.


 

The author's opinion: coroutines on bare metal are dangerous


 

Personally, the author has little to do with coroutines in dynamic languages, but he is extremely wary of any flirting with the stack at the level of machine code.


 

Several theses:


 
  1.  
  2. It is required to select the stack:
     
    •  
    • stack on the heap has a number of shortcomings: the problems of timely detection of overflow, damage to neighbors and other problems of reliability /security,  
    • A protected stack requires at least one page of physical memory, one conditional page, and additional overhead for each call. async functions: 4 + KB (minimum) + increased system limits,  
    • in the final analysis, it can be that a large part of the allocated memory is not used during the coroutine idle time.  
     
  3. It is necessary to implement the complex logic of saving, restoring and deleting the state of coroutines:
     
    •  
    • for each case of processor architecture (even model) and binary interface (ABI): example ,  
    • new or optional features of the architecture introduce potentially latent problems (for example, Intel TSX, co-processors ARM or MIPS),  
    • other potential problems due to closed documentation of proprietary systems (Boost documentation refers to this).  
     
  4. Potential problems with dynamic analysis tools and with security in general:
     
    •  
    • for example, integration with Valgrind is required because of the same hopping stacks,  
    • It's hard to say for antiviruses, but probably they do not particularly like it on the example of problems with JVM in the past,  
    • I'm sure there will be new types of attacks and vulnerabilities will be revealed that are related to the implementation of coroutines.  
     

 

The author's opinion: generators and yield the principal evil is


 

This seemingly third-party theme is directly related to the concept of coroutines and the properties of "continuation."


 

If in brief, then for any collection there should be a full iterator. For what purpose to create a problem of the cut off iterator-generator - it is not clear. For example, a case with range () in Python rather exclusive vypendrezh, than justification of technical complication.


 

If the case is an infinite generator, then the logic of its implementation is elementary. Why create additional technical difficulties in order to push the infinite continuous cycle inside.


 

The only valid later appeared justification, which lead supporters coroutines - it's all sorts of flow parsers with inverting control. In fact, this is a narrow specialized case of solving single problems of the level of libraries, rather than the business logic of applications. At the same time, there is an elegant, simple and more descriptive solution through finite automata. The scope of these technical problems is much less than the area of ​​banal business logic.


 

In fact, the problem to be solved is sucked from the finger and requires a relatively serious effort for the initial implementation and sustained support. So much so that some projects can impose a ban on the use of coroutines of the level of machine code according to the example of the ban on goto or the use of dynamic allocation of memory in individual industries.


 

The author's opinion: model async /await on Promise from ECMAScript is more reliable, but requires adaptation of


 

Unlike the continued coroutines, in this model the pieces of code are secretly divided into uninterrupted blocks, designed in the form of anonymous functions. In C ++, this is not entirely appropriate due to the memory management features, for example:


 
    struct SomeObject {
using Value = std :: vector ;
Promise funcPromise () {
return Promise.resolved (value_);
}
void funcCallback (std :: function && cb, const Value & val) {
somehow_call_later (cb);
}
Value value_;
};};
Promise example () {
SomeObject some_obj;
return some_obj.funcPromise ()
.catch ([](const std :: exception & e) {
//
})
.then ([&](SomeObject :: value && val) {
return Promise ([&](Resolve && resolve, Reject &&) {
some_obj.funcCallback (resolve, val);
});
});
}

 

Firstly, some_obj will be destroyed when exiting from example () and before the call of lambda functions.


 

Secondly, lambda functions with the capture of variables or references are objects and covertly add copy /move, which can adversely affect performance with a large number of captures and the need to allocate memory on the heap during type erasure in the usual std :: function .


 

Thirdly, the interface itself is Promise conceived on the concept of "promise" of the result, rather than the consistent implementation of business logic.


 

A schematic NOT optimal solution might look something like this:


 
    Promise example () {
struct LocalContext {
SomeObject some_obj;
};};
auto ctx = std :: make_shared ();
return some_obj.funcPromise ()
.catch ([](const std :: exception & e) {
//
})
.then ([ctx](SomeObject :: Value && val) {
.structure LocalContext2 {
LocalContext2 (std :: shared_ptr && ctx, SomeObject :: Value && val):
.ctx (ctx), val (val)
{}
.
.std :: shared_ptr ctx;
SomeObject :: Value val;
};
Auto ctx2 = std :: make_shared (
.std :: move (ctx),
std :: forward (val)
);
.
return Promise ([ctx2](Resolve && resolve, Reject &&) {
.ctx2-> ctx-> some_obj.funcCallback ([ctx2, resolve]() {resolve ();}, val);
});
});
}

 

Note: std :: move instead of std :: shared_ptr is not suitable because of the inability to transfer into several lambdas immediately and the growth of their size.


 

With the addition of async /await Asynchronous "horrors" come in a digestible state:


 
    async void example () {
SomeObject some_obj;
try {
SomeObject :: Value val = await some_obj.func ();
} catch (const std :: exception & e) (
//
}
.
//Capture "async context"
return Promise ([async](Resolve && resolve, Reject &&) {
some_obj. funcCallback ([async]() {resolve ();}, val);
});
}
.
.
 

The author's opinion: the coroutine planner is a search of


 

Some critics call the problem the lack of a scheduler and the "unfair" use of CPU resources. Perhaps a more serious problem is the locality of the data and the efficient use of the processor's cache.


 

On the first issue: the prioritization at the level of individual coroutines looks like a big overhead. Instead, they can operate in common for a specific unified task. This is done with traffic flows.


 

This is possible by creating separate instances of the Event Loop with its own "iron" threads and planning at the OS level. The second option is to synchronize coroutines with respect to the restrictive (and) performance of the primitive (Mutex, Throttle).


 

Asynchronous programming does not make the processor's resources rubber, and requires absolutely normal limitations on the number of simultaneously processed tasks and the limitation of the overall execution time.


 

Protection from long blocking on one coroutine requires the same measures as with callbacks - avoid blocking system calls and long data processing cyclesx.


 

The second problem requires research, but at least the coroutine stacks and the details of the Future /Promise implementation already violate the locality of the data. There is an opportunity to try to continue the execution of the same coroutine, if Future already matters. It requires some mechanism for calculating the execution time or the number of such continuations in order not to allow one coroutine to capture the entire processor time. This can either not give a result, or give a very two-fold result, depending on the size of the processor's cache and the number of threads.


 

There is also a third point - many implementations of coroutine planners allow them to be executed on different processor cores, which in turn adds problems due to mandatory synchronization when accessing shared resources. In the case of a single Event Loop stream, such synchronization is only required at a logical level, because Each synchronous callback block is guaranteed to work without race with others.


 

The author's opinion: everything is fine in the measure


 

Presence of streams in modern OS does not cancel the use of separate processes. Also, processing a large number of clients in Event Loop does not cancel the use of separate "iron" flows for other needs.


 

In any case, coroutines and various variants of Event Loops complicate the debugging process without the necessary support in the tools, and with local variables on the coroutine stack everything becomes even more difficult - they are almost impossible to reach.


 


 
Asynchronous business logic these days

 

FutoIn AsyncSteps - an alternative to coroutines


 

As a basis, let's take the well-proven Event Loop pattern and organize the ECMAScript (jаvascript) Promise callback scheme.


 

From the point of view of execution planning, we are interested in the following actions from Event Loop:


 
  1.  
  2. Immediate callback Handle immediate (callack) with the requirement of a pure call stack.  
  3. Delayed callback Handle deferred (delay, callback) .  
  4. Cancellation of the callback handle.cancel () .  

 

So we get an interface with the name AsyncTool , which can be implemented in a variety of ways, including on top of existing proven developments. Direct relationship to writing business logic, he does not have, so we will not go into further details.


 

Step tree:


 

In the concept of AsyncSteps, an abstract tree of synchronous steps is built and executed through the depth in the creation sequence. The steps of each deeper level are dynamically set as the passage proceeds.


 

All interaction occurs through a single interface AsyncSteps , which by convention is transmitted by the first parameter in each step. By convention, the name of the parameter is asi or obsolete as . This approach allows you to almost completely break the relationship between a specific implementation and writing business logic in plug-ins and libraries.


 

In canonical implementations, each step receives its own instance of an object that implements AsyncSteps , which allows you to timely monitor logical errors in the use of the interface.


 

Abstract example:


 
    asi.add (//Level 0 step 1
func (asi) {
print ("Level 0 func")
asi.add (//Level 1 step 1
func (asi) {
print ( "Level 1 func")
Asi.error ("MyError")
},
onerror (asi, error) {//Level 1 on 1 error
asm.error ("NewError")
}
)
},
onerror (asi, error) {//Level 0 step 1 catch
print ("Level 0 onerror:" + error)
if (error strequal "NewError") {
asi.success ("Prm", 12?[1, 2, 3], True)
}
}
)
asi.add (//Level 0 step 2
func (asi, str_param, int_param, array_param) {
print ("Level 0 func2:" + param)
}
)

 

Result of the execution:


 
    Level 0 func 1
Level 1 func 1
Level 1 onerror 1: MyError
Level 0 onerror 1: NewError
Level 0 func 2: Prm

 

In synchronous form it would look like this:


 
    str_res, int_res, array_res, bool_res //undefined
try {
//Level 0 step 1
print ("Level 0 func 1")
try {
//Level 1 step 1
print ("Level 1 func 1")
throw "MyError"
} catch (error) {
//Level 1 step 1 catch
print ("Level 1 onerror 1:" + error)
throw "NewError"
}
} catch (error) {
//Level 0 step 1 catch
print ("Level 0 onerror 1:" + error)
if (error strequal "NewError") {
str_res = "Prm"
int_res = 123
array_res =[1, 2, 3]
bool_res = true
} else {
re-throw
}
}
{
//Level 0 step 2
print ("Level 0 func 2:" + str_res)
}

 

Immediately visible maximum mimicry of traditional synchronous code, which should help in readability.


 

From the point of view of business logic, grows over time. big com of requirements , but we can divide it into easily understood parts. Described below, the result of running in practice for four years.


 

The basic execution time API:


 
  1.  
  2. add (func[, onerror]) - imitation of try-catch .  
  3. success ([args]) - an explicit indication of a successful completion:
     
    •  
    • is assumed by default,  
    • can pass the results to the entrance to the next step.  
     
  4. error (code[, reason) — прерывание выполнения с ошибкой:
    • code — имеет строковой тип чтобы лучше интегрироваться с сетевыми протоколами в микросервисной архитектуре,

    • reason — произвольное пояснение для человека.


  5. state() — аналог Thread Local Storage. Предопределённые ассоциативные ключи:
    • error_info — пояснение последний ошибки для человека,

    • last_exception — указатель на объект последнего исключения,

    • async_stack — стек асинхронных вызовов на сколько позволяет технология,

    • остальное — задаётся пользователем.


Предыдуший пример уже с реальным С++ кодом и некоторыми дополнительными фичами:


#include 

using namespace futoin;

void some_api(IAsyncSteps& asi) {
asi.add(
[](IAsyncSteps & asi) {
.std :: cout "Level 0 func 1" std :: endl;
.
asi.add (
.[](IAsyncSteps & asi) {
.std :: cout "Level 1 func 1" std :: endl;
asi.error ("MyError");
},
.[](IAsyncSteps & asi, ErrorCode code) {
.std :: cout "Level 1 onerror 1: "code std :: endl;
asi.error (" NewError "," Human-readable description ");
}
);
},
.[](IAsyncSteps & asi, ErrorCode code) {
std :: cout "Level 0 onerror 1:" code std :: endl;
.
if (code == "NewError") {
//Human-readable error info
assert (asi.state (). error_info == 3r 3r32092. "Human-readable description");
//Last exception thrown is also available in state
std :: exception_ptr e = asi.state (). last_exception;
//NOTE: smart conversion of "const char *"
asi.success ("Prm", 12? std :: vector ({? ? 3}, true));
}
}
);
asi.add ( ?[](IAsyncSteps & asi, const futoin :: string & str_res, int intres, std :: vector && arr_res) {
.std :: cout "Level 0 func 2:" str_res std :: endl ;
}
);
}

 

API for creating cycles:


 
  1.  
  2. loop (func,[, label]) - a step with an infinitely repeated body.  
  3. forEach (map | list, func[, label]) - step-iteration on the collection object.  
  4. repeat (count, func[, label]) - step-iteration the specified number of times.  
  5. break ([label]) - an analog of the traditional cycle interruption.  
  6. continue ([label]) - an analog of the traditional continuation of the cycle with a new iteration.  

 

The specification offers alternative names breakLoop , continueLoop and others in case of conflict with reserved words.


 

C ++ example:


 
    asi.loop ([](IAsyncSteps & asi) {
//infinite loop
asi.breakLoop ();
});
asi.repeat (1?[](IAsyncSteps & asi, size_t i) {
//range loop from i = 0 to i = 9 (inclusive)
asi.continueLoop ();
});
asi.forEach (
.std :: vector {? ? 3},
.[](IAsyncSteps & asi, size_t i, intv) {
//Iteration of vector-like and list-like objects
.});
asi.forEach (
.std :: list {"1", "2", "3"},
.[](IAsyncSteps & asi, size_t i, const futoin :: string & v) {
//Iteration of vector-like and list-like objects
});
asi.forEach ( ? std :: map (), ?[](IAsyncSteps & asi, ? const futoin :: string & key, ? const futoin :: string & v) {
//Iteration of map- like objects
});
std :: map non_const_map;
asi.forEach ( ? non_const_map, ?[](IAsyncSteps & asi, const std :: string & key, futoin :: string & v) {
//Iteration of map-like objects, note the value reference type
}) ;

 

API integration with external events:


 
  1.  
  2. setTimeout (timeout_ms) - causes error Timeout at the expiration of time, if the step and its subtree have not completed execution.  
  3. setCancel (handler) - sets the cancel handler, which is called when the thread is canceled and when the stack of asynchronous steps is expanded during error processing.  
  4. waitExternal () - simple waiting for an external event.
     
    •  
    • Note: it is safe to use only in technologies with a garbage collector.  
     

 

Calling any of these functions makes it necessary to explicitly call success () .


 

C ++ example:


 
    asi.add ([](IAsyncSteps & asi) {
auto handle = schedule_external_callback ([&](bool err) {
if (err) {
try {
asi.error ("ExternalError");
} catch () {
//pass
}
} else {
asi.success ();
}
});
.
asi.setCancel ([=](IAsyncSteps & asi) {external_cancel (handle);});
});
asi.add (
.[](IAsyncSteps & asi) {
//Raises Timeout error after the specified period
.asi.setTimeout (std :: chrono :: seconds {10});
.
asi.loop ([](IAsyncSteps & asi) {
//infinite loop
});
},
.[](IAsyncSteps & asi, ErrorCode code) {
If (code == futoin :: errors :: Timeout) {
. asi ();
}
});

 

ECMAScript example:


 
    asi.add ((asi) => {
asi.waitExternal (); //disable implicit success ()
.
some_obj.read ((err, data) => {
if (! asi.state) {
//ignore as AsyncSteps execution got canceled
} Else if (err) {
Try {
Asi.error ('IOError', err);
} Catch (_) {
//ignore error thrown as there are no
//AsyncSteps frames on stack .
}
} else {
asi.success (data);
}
});
});

 

API integration with Future /Promise:


 
  1.  
  2. await (promise_future[, on_error]) - expectation of Future /Promise as a step.  
  3. promise () - turns the entire execution thread into Future /Promise, is used instead of execute () .  

 

C ++ example:


 
   [](IAsyncSteps & asi) {
//Proper way to create new AsyncSteps instances
//without hard dependency on implementation.
auto new_steps = asi.newInstance ();
new_steps-> add ([](IAsyncSteps & asi) {});
//Can be called outside of AsyncSteps event loop
//new_steps.promise (). wait ();
//or
//new_steps.promise () .get ();
//Proper way to wait for standard std :: future
asi.await (new_steps-> promise ());
//Ensure instance lifetime
asi.state ()["some_obj"]= std :: move (new_steps);
};};

 

API for monitoring the flow of business logic:


 
  1.  
  2. AsyncSteps (AsyncTool &) - A constructor that binds the flow of execution to a specific Event Loop.  
  3. execute () - runs the thread of execution.  
  4. cancel () - Cancels the flow of execution.  

 

A specific implementation of the interface is already required here.


 

C ++ example:


 
    #include  
#include
void example () {
futoin :: ri :: AsyncTool at;
futoin :: ri :: AsyncSteps asi {at};
asi.loop ([&](futoin :: IAsyncSteps & asi) {
//Some infinite loop logic
});
asi.execute ();
std :: this_thread :: sleep_for (std :: chrono :: seconds {10});
asi.cancel (); //called in d-tor by fact
}

 

other API:


 
  1.  
  2. newInstance () - allows you to create a new thread of execution without direct dependence on the implementation.  
  3. sync (object, func, onerror) - the same, but with synchronization with respect to the object that implements the corresponding interface.  
  4. parallel ([on_error]) - Special add () , the substeps of which are separate streams of AsyncSteps:
     
    •  
    • all streams have a common state () ,  
    • the parent thread continues execution at the completion of all its children,  
    • A non-intercepted error in any child immediately overrides all other child threads.  
     

 

Examples of C ++:


 
    #include  
using namespace futoin;
ri :: Mutex mtx_a;
void sync_example (IAsyncSteps & asi) {
asi.sync (mtx_a,[](IAsyncSteps & asi) {
//inner step in the section
.
//This synchronization is NOOP for already
//acquired Mutex .
Asi.sync (mtx_a,[](IAsyncSteps & asi) {
});
});
});
}
void parallel_example (IAsyncSteps & asi) {
using OrderVector = std :: vector ;
asi.state ("order", OrderVector {});
auto & p = asi.parallel ([](IAsyncSteps & asi, ErrorCode) {
//Overall error handler
asi.success ();
});
p.add ([](IAsyncSteps & asi) {
//regular flow
asi.state ("order"). push_back (1);
.
asi.add ([](IAsyncSteps & asi) {
Asi.state ("Order"). Push_back (4);
});
});
p.add ([](IAsyncSteps & asi) {
asi.state ("order"). push_back (2);
.
.si.add ([](IAsyncSteps & asi) {
asi.state . ("order"). push_back (5);
asi.error ("SomeError");
});
});
p.add ([](IAsyncSteps & asi) {
asi.state ("order"). push_back (3);
.
.si.add ([](IAsyncSteps & asi) {
asi.state . ("order"). push_back (6);
});
});
asi.add ([](IAsyncSteps & asi) {
asi.state ("order"); //? ? ? ? 5
});
};};

 

Standard primitives for synchronization


 
  1.  
  2. Mutex - Limits simultaneous execution in N threads with a queue in Q , the default is N = ? Q = unlimited .  
  3. Throttle - limits the number of inputs N in the period P with a queue at Q , the default is N = ? P = 1s, Q = 0 .  
  4. Limiter - combination of Mutex and Throttle , which is typically used at the input of processing external requests and when calling external systems for the purpose of stable work under load.  

 

In case of exceeding the queue limits, the error is raised. DefenseRejected , the meaning of which is clear from the description of Limiter .


 

The key advantages of


 

The AsyncSteps concept was not an end in itself, but was born out of the need for more controlled asynchronous execution of programs in terms of time limit, cancellation, and overall connectivity of individual callbacks. None of the universal solutions at the time and now does not provide the same functionality. Therefore:


 

Single Specification FTN12 for all technologies - Rapid adaptation for developers when switching from one technology to another.


 

Integration of canceling setCancel () - allows you to undo all external requests and clean up resources in a clear and secure way with an external cancel or runtime error. This avoids the problem of asynchronous execution of heavy tasks, the result of which is no longer required. itanalogue of RAII and atexit () in traditional programming.


 

Directly cancel execution of cancel () - Typically used when the client is disconnected, the maximum deadline for the query has expired or other reasons for termination. This is an analog of SIGTERM or pthread_cancel () , both of which are very specific in implementation.


 

Step cancel timers setTimeout () - Typically used to limit the overall execution time of the query and to limit the timeout for subqueries and external events. They act on the entire subtree, discards the intercepted error "Timeout".


 

Integration with other technologies of asynchronous programming - the use of FutoIn AsyncSteps does not require the abandonment of already used technologies and does not require the development of new libraries.


 

Universal implementation at the level of a specific programming language - there is no need for specific and potentially dangerous ABI-level manipulations at the machine code level that are required for coroutines. Well suited for embedded and secure on a defective MMU.


 


 

 

The source of the picture is


 

To the numbers


 

For tests, Intel Xeon E3-1245v2 /DDR1333 with Debian Stretch and the latest firmware update are used.


 

Five variants are compared:


 
  1.  
  2. Boost.Fiber with protected_fixedsize_stack .  
  3. Boost.Fiber with pooled_fixedsize_stack and allocation on a common heap.  
  4. FutoIn AsyncSteps in the standard version.  
  5. FutoIn AsyncSteps in a dynamically disabled pool of memory ( .FUTOIN_USE_MEMPOOL = false ).
     
    •  
    • is given only for evidence of the effectiveness of futoin :: IMemPool .  
     
  6. FutoIn NitroSteps <> — альтернативная реализация со статическим выделением всех буферов во время создания объекта.
     
    •  
    • Specific limit parameters are specified in the form of advanced template parameters.  
     

 

In view of the functional limitations of Boost.Fiber, the following performance indicators are compared:


 
  1.  
  2. Consistent creation and implementation of 1 million flows.  
  3. Parallel creation of flows with a limit of 30 thousand and execution of 1 million flows.
     
    •  
    • the limitation of 30 thousand is based on the need to call mmap () /mprotect () for boost :: fiber :: protected_fixedsize_stack .  
    • A large digit is also selected for pressure on the processor's cache.  
     
  4. Parallel creation of 30 thousand streams and 10 million switching operations in anticipation of an external event.
     
    •  
    • in both cases, the "external" event is satisfied in a separate thread within the same technology.  
     

 

One kernel and one "iron" thread are used, because this allows you to achieve maximum performance, excluding racing on the backs and atomic operations. The best values ​​from the series of tests go to the result. The stability of the indicators is checked.


 

The assembly is done with GCC ???. The results with Clang and tcmalloc were also checked, but the differences are insignificant for the article.


 

The source code for the tests is available at GitHub and GitLab .


 

1. Consecutive creation of


 
Technology The time is Hz
Boost.Fiber protected 4.8s ???Hz
Boost.Fiber pooled ???s ???Hz
FutoIn AsyncSteps ???s ???Hz
FutoIn AsyncSteps no mempool ???s ???Hz
FutoIn NitroSteps ???s ???Hz

 

 

More is better.


 

Here the main loss of Boost.Fiber is due to system calls to work with memory pages, but even less secure pooled_fixedsize_stack It turns out to be slower than the standard implementation of AsyncSteps.


 

2. Parallel creation and execution of


 
Technology The time is Hz
Boost.Fiber protected ???s ???Hz
Boost.Fiber pooled ???s ???Hz
FutoIn AsyncSteps ???s ???Hz
FutoIn AsyncSteps no mempool ???s ???Hz
FutoIn NitroSteps ???s ???Hz

 

 

More is better.


 

Here are the same problems, but we see a significant slowdown in comparison with the first test. More detailed analysis is needed, but the theoretical guess indicates inefficiency of caching-selective values ​​of the number of parallel streams give a picture of a function with a lot of explicit kinks.


 

3. Parallel cycles


 
Technology The time is Hz
Boost.Fiber protected ???s ???Hz
Boost.Fiber pooled ???s ???Hz
FutoIn AsyncSteps ???s ???Hz
FutoIn AsyncSteps no mempool ???s ???Hz
FutoIn NitroSteps ???s ???Hz

 

 

More is better.


 

Removing the overhead creator, we see that in the bare-metal stream switching, Boost.Fiber starts to win from the standard AsyncSteps implementation, but it loses significantly in NitroSteps.


 

Using memory (by RSS)


 
Technology Memory
Boost.Fiber protected 124M
Boost.Fiber pooled 505M
FutoIn AsyncSteps 124M
FutoIn AsyncSteps no mempool 84M
FutoIn NitroSteps 115M

 

 

Less is better.


 

And again, Boost.Fiber has nothing to be proud of.


 

Bonus: tests for Node.js


 

Only two tests are also due to the limitations of Promise : creation + execution and loops of 10 thousand iterations. Each test is 10 seconds. The average values ​​in Hz of the second pass are taken after the optimizing JIT at NODE_ENV = production , using the package @ futoin /optihelp .


 

The source code is also at GitHub and GitLab . The versions Node.js v??? and v???.? installed through

  • ? are used. FutoIn CID .
     
Tech Simple Loop
Node.js v10
FutoIn AsyncSteps ???Hz ???Hz
async /await ???Hz ???Hz
Node.js v8
FutoIn AsyncSteps ???Hz ???Hz
async /await ???Hz ???Hz

 

 

 

More is better.


 

The sudden rout of async /await ? Yes, but in V8 for Node.js v10 pulled up the optimization cycles and it became a little better.


 

It is worth adding that the implementation of Promise and async /await blocks Node.js Event Loop. An infinite loop without waiting for an external event will simply hang the process (
? proof ), But this does not happen with FutoIn AsyncSteps.


 

The periodic output from AsyncSteps in the Node.js Event Loop is the cause of the false victory of async /await in the test-cycle on Node.js v10.


 

I will make a reservation that comparing performance indicators with C ++ will not be correct - different implementation of the testing methodology. For approximation, the results of testing the Node.js cycles should be multiplied by 10 thousand


 

Conclusions


 

In the example of C ++, FutoIn AsyncSteps and Boost.Fiber show similar long-term performance and memory consumption, but at launch Boost.Fiber seriously loses and is limited by system limits by the number of mmap () /mprotect .


 

The maximum performance depends on the effective use of the cache, but in real life, business logic data can exert more pressure on the cache than coroutine implementation. The question of a full-fledged scheduler remains open.


 

FutoIn AsyncSteps for jаvascript is superior to async /await in performance even in the latest version of Node.js v10.


 

Ultimately, the main load must be directly from the business logic, and not from the implementation of the concept of asynchronous programming. Therefore, minor deviations should not be an essential argument.


 

Reasonably written business logic introduces asynchronous transitions only when it is meant waiting for an external event in any form or when the amount of data being processed is too large and blocks the "iron" execution thread for a long time. The exception to this rule is the writing of library APIs.


 


 

Conclusion


 

Fundamentally, the FutoIn AsyncSteps interface is simpler, more understandable and more functional than any of the competitors without the "sugar" async /await . In addition, it is implemented by standard tools and unified for all technologies. As in the case with Promise from ECMAScript, AsyncSteps can get its "sugar" and its support in compilers or runtime environments of almost any language.
 
Performance measurements of not highly optimized implementations show comparable and even superior results with respect to alternative technologies. The flexibility of the possible implementation is proved by two different approaches of AsyncSteps and NitroSteps for a single interface.
 
In addition to the above mentioned specifications , is oriented to the developer-user Documentation .
 
On request, it is possible to implement a version for Java /JVM or any other language with adequate anonymous functions - otherwise the code readability drops. Any help to the project is welcome.
 
If you really like this alternative, then do not hesitate to put the asterisk on GitHub and /or GitLab .

It may be interesting

  • Comments
  • About article
  • Similar news
This publication has no comments.

weber

Author

25-09-2018, 19:14

Publication Date

Programming / Open source / Node.JS

Category
  • Comments: 0
  • Views: 313
Microsoft told how to solve the problem
To eliminate Specter and Meltdown, you
Asynchronous Python: various forms of
Release Node.js 10.5: multithreading
Networks that form the intellect
Efficient use of memory for parallel I
Write a comment
Name:*
E-Mail:


Comments
this is really nice to read..informative post is very good to read..thanks a lot! How is the cost of house cleaning calculated?
Today, 17:14

Legend SEO

It’s very informative and you are obviously very knowledgeable in this area. You have opened my eyes to varying views on this topic with interesting and solid content.

entegrasyon programları
Today, 17:09

taxiseo2

I am really enjoying reading your well written articles. It looks like you spend a lot of effort and time on your blog. I have bookmarked it and I am looking forward to reading new articles. Keep up the good work.

entegrasyon programları
Today, 17:02

taxiseo2

I found so many interesting stuff in your blog especially its discussion. From the tons of comments on your articles, I guess I am not the only one having all the enjoyment here! keep up the good work...먹튀

Today, 16:50

raymond weber

Lose Weight Market provides the best fitness tips, workout guides, keto recipes and diet plans, yoga workout routine and plans, healthy recipes, and more! Check Out: Lose Weight Market


Corvus Health provides medical training services as well as recruiting high quality health workers for you or placing our own best team in your facility. Check Out: Health Workforce Recruitment


Today, 19:37

noorseo

Adv
Website for web developers. New scripts, best ideas, programming tips. How to write a script for you here, we have a lot of information about various programming languages. You are a webmaster or a beginner programmer, it does not matter, useful articles will help to make your favorite business faster.

Login

Registration Forgot password