Good afternoon, today I want to continue to talk about my library, in the hope of finding supporters of the idea.
This post is a continuation of
Transcend Library 4: Introduction , or rather the logical development of the discussion in the comments.
Why am I writing a new post instead of opening the code
- has a simple cause. The reasons.
- After the next architecture refactor (June 2012) the library does not work. More precisely, half of the methods simply do not exist. I understand that a quarter is solved by copy-paste from previous versions, but the fact remains.
- Refactoring is not complete yet. Half of the library was translated under the new standard, and half is only being prepared, they simply did not reach out.
- The code is not documented, and at this stage it is not clear in some places.
- Yes, and it will be easier to start the development knowing at least in general terms the idea than gnawing the code from scratch.
Disclaimer
I am ambitious, young, stupid and self-confident. Maximalism is not yet dead, and at times arises. But still, after more or less sensible reasoning, opening the code and going into Open Source seems to me a simpler and more realistic way to find fellow developers than to make money from the first sales and hire staff.
Patterns
Most of what I used has a widespread pattern. Something is not, but very common. In any case, I hope that any site will be clear to those who do not know them. For those who are familiar - I will next add the name.
')
Tl4
Asynchrony
Nature of the network
The nature of the network is asynchronous. There are no concepts of "simultaneously" and "instantly." All effects of calling functions occur with delays depending on many reasons. Your right to wait for the result, or perform your actions at this time (blocking and non-blocking sockets), but the fact will not change. In the meantime, how you want to open a connection, and how it actually opens will take considerable time. And that's bad.
But there is good news, you can simultaneously order a lot of actions to be done, and wait for the first one to end. At the same time, the number of actions does not greatly affect the execution time of each.
All this directly asks to design a library with event support, but that's not all.
Request congestion
There is another price for using the network - every call is worth the time. Big time. It is spent on the release of the resource, the resolution in the operating system queue and the like. For example, we want to make 1000 dns requests. Let each call cost 1ms (adequate time, sometimes more), this means that we will lose a second only on calls. At this time, our application running in the same thread will “slacken off” via FPS, and the user will see the hang-up on level ground.
And in fact, the driver makes 50 (number from the ceiling) requests every second, for all applications in the system.
I understand that somewhere it may be justified to do this (and there is a setting that would happen), but the system was designed for realtime applications, and in them this is unacceptable.
The order of the task
When you request an OpenConnection, the library does nothing other than allocating memory and setting the job in turn. All operations take place in the local address space and local stream, because of this they are fast.
The actual delegation occurs when calling Response, which scans the tasks and executes one by one, before the timeout.
In our example, this allows, after the first 50 requests, to start establishing a connection, and it is possible (if it has time) to begin (or even complete) the exchange of information before receiving all 1000 addresses.
The internal structure can be compared to an event-driven architecture, except that the notification of events does not occur according to the observer pattern.The priority of the job
Let us play an online game like WoW. Also, let the administration not want to sell new items through patches. What is the use of downloading to millions of players a sword that God forbid will receive 10 zadrots, but will God see 1000 passers-by?
Let this sword fell in battle, when the battle is not over. Let the same voice communication sewn into the game.
Immediately visible several simultaneous data streams: damage to the boss, the level of lives of all around, their movements, voice and sword. (Data sorted by increase in volume)
The transfer priorities should be the following: displacement, standard of living, voice, damage, sword.
It would be a shame if the library did not provide such functionality.
Related tasks
The feature is not provided either in the release or in the architecture, but it will fit in harmoniously there.The characteristics of the sword, the texture, the model, and the icon without each other do not make much sense. But at the same time, it would be logical to transfer the characteristics and the pictogram, making it possible to use a weapon, without waiting for the model to load.
The texture and model will inherit the priority of the parent, when transmitted, and their priority will be set by relative offset. It remains to choose the coefficients so that the transfer would occur in a timely manner.
Dynamic priority
Let our multi-gigabyte voyage come to an end, and have a low priority in the system. And at this very moment (
99.9% ) it became necessary to transfer several megabytes of urgent data. Despite the fact that the imaginary gig turned into a dozen kilobytes.
Here it would be extremely reasonable to change the priority of the task depending on the remaining and buffered amount of data.
I don’t like my current decision in the library, I need to think about it and if I can improve it.Memory device
Data storage model
In a complex system, you may need access to the same resources from different places. For example, a chat_listener and attack_listener will want to send a message. And each has its own set of connections.
To avoid duplication and desynchronization of data, storage of keys to the database in objects given to the user is used (the
closest thing here is Bridge , only for stored data ).
The database is one for the virtual network (this is why data operations within the same network are not safe), and, like all data stored in it, is managed through
shared_self_controlled .
Responsibility for memory
Due to the nature of the task, the user cannot retain full control over the allocated memory.
The idea of ​​the library - “to put on the shipment and be sure that it will reach” is somewhat poisoned, if you also have to watch it and when it reaches and delete it.
Oddly enough, the library in the general case also does not know when exactly the object should be deleted. She knows that he should be transferred, knows when this transfer will end, but should it be deleted after that? It knows only the object. (
oh gods he uses delete this )
Garbage collector
Big words that do not reflect the essence of what is happening. But it is remotely so - as soon as the object is not needed by anyone, it is deleted.
You can cut it off with shared_ptr , except that the object itself is both a container and an object.shared_self_conrolled literally means the following. An object can be separated (as opposed to a simple self_controlled), and it must follow when it needs to self-destruct (as opposed to shared).
Once the object has been transferred, the library does not need it; it deletes itself from the list of users. If they become zero - no one else can turn to the object, it is not needed.
Data processing
Deferred reading
Suppose we need to transfer a file of several megabytes in size per kilobyte connection. Should the file be uploaded to memory for a few minutes? Or even so, our file weighs several gigabytes.
The point of this example is that data is not always worth processing instantly. It is better to postpone work for tomorrow, because today the end of the world may come, right? ;)
Thread tension
If we want to pull the machine by the rope (such a toy), it will go only when the entire thread is pulled. Strength will be transferred from one layer to another, evenly. If in some place the stiffness is less than others - it will stretch, taking all the load on itself.
When data handlers line up we are not interested in what happens in the middle. Only movement at the ends.
Suppose we have a decompress handler that decompresses the compressed data, and the client needs 30 bytes. During decompression, only 5 bytes of input data were used to obtain this volume. The moral is that each link knows how much is needed from the next, and nothing more. This is reflected in the library - the whole calculation takes place on request.
Caching
Suppose we need to process data that is requested every few seconds. It is logical to create such a chain:
download => calculation => save to temporary file => download from temporary file => transfer
In this case, only the data necessary for processing will be stored in memory, and only ready for transmission in the file.
It is foolish to wait a few seconds to find out how much data is needed, it is necessary that they are ready.
The logical solution is obvious - in your free time, drag the data so that it is the minimum number at the calculation and loading stage, and the maximum in the temporary file.
For this, there are container parameters: minimize to, maximize to. (This means that if the limit is violated, it will stop sending requests further)
Another great example is the proxy. It is not necessary to download data faster than we can issue it, it will increase the memory consumption on the local machine, it does not suit us.
Conclusion
I hope this post I was able to reveal a bit of those ideas that were the reason for creating the library, and why it seems to me competitively capable. I also believe that several brave souls will want to take part in the creation of something so wonderful (IMHO) as the architecture proposed here.
It’s definitely not all the ideas hidden in the project, some of them went into the subconscious and hid in the bone marrow, it seems obvious. This is only the most significant, and come to mind.
“We are taught to remember not a project of a person, but an idea, because a person is weak and can be caught, he can be killed and put to oblivion. But the idea and 400 years later can change the world. " " V means Vendetta "
Have a great weekend.