Tag Archive | C++

Multitasking styles, event loops and asynchronous programming

There was a text that I’ve previously published here in this blog about asynchronous programming, but it was written in Portuguese. Now has come the time that I think this text should be available also in English. Translation follows below. Actually is not a “translation” per si, as I adapted the text to be more pleasant when written in English.

One of the subjects that interest me the most in programming is asynchronous programming. This is a subject that I got in touch since I started to play with Qt around 2010, but I slowly moved to new experiences and “paradigms” of asynchronous programming. Node.js and Boost.Asio were other important experiences worth mentioning. The subject caught me a lot and it was the stimulus for me to study a little of computer architecture and operating systems.


Several times we stumble upon with problems that demand continuously handling several jobs (e.g. handling network events to mutate local files). Intuitively we may be tempted to use threads, as there are several “parallel” jobs. However, not all jobs executed by the computer are done so exclusively in the CPU.

There are other components beside the CPU. Components that are not programmable and that do not execute your algorithm. Components that are usually slower and do other jobs (e.g. converting a digital signal into an analog one). Also, the communication with these components usually happen serially, through the fetch-execute-check-interrupt cycle. There is a simplification in this argument, but the fact that you don’t read two different files from the same hard drive in parallel remains. Summarizing, using threads isn’t a “natural” abstraction to the problem, as it doesn’t “fit” and/or design the same characteristics. Using threads can add a complex and unnecessary overhead.

“If all you have is a hammer, everything looks like a nail”

Another reason to avoid threads as an answer to the problem is that soon you’ll have more threads than CPU/cores and will face the C10K problem. Even if it didn’t add an absurd overhead, just the fact that you need more threads than available CPUs will make your solution more restrict, as it won’t work on bare-metal environments (i.e. that lacks a modern operating system or a scheduler).

A great performance problem that threads add comes from the fact that they demand a kernel context-switch. Of course this isn’t the only problem, because there is also the cost involved in creating the thread, which might have a short lifetime and spend most of its lifetime sleeping. The own process of creating a thread isn’t completely scalable, because it requires the stack allocation, but the memory is a global resource and right here we have a point of contention.

The performance problem of a kernel context-switch reminds the performance problem of the function calling conventions faced by compilers, but worse.

Functions are isolated and encapsulated units and as such they should behave. When a function is called, the current function doesn’t know which registers will be used by the new function. The current function doesn’t hold the information of which of the CPU registers will be overridden during the new function lifetime. Therefore, the function calling conventions add two new points to do extra processing. One point to save state into the stack and one to restore. That’s why some programmers are so obsessed into function inlining.

The context-switch problem is worse, because the kernel must save the values of all registers and there is also the overhead of the scheduler and the context-switch itself. Processes would be even worse, as there would be the need to reconfigure the MMU. This multitasking style is given the name of preemptive multitasking. I won’t go into details, but you can always dig more into computer architecture and operating systems books (and beyond).

It’s possible to obtain concurrency, which is the property to execute several jobs in the same period of time, without real parallelism. When we do this tasks that are more IO-oriented, it can be interesting to abandon parallelism to achieve more scalability, avoiding the C10K problem. And if a new design is required, we could catch the opportunity to also take into account cooperative multitasking and obtain a result that is even better than the initially planned.

The event loop

One approach that I see, and I see used more in games than anywhere else, is the event loop approach. It’s this approach that we’ll see first.

There is this library, the SDL low level library, whose purpose is to be just a multimedia layer abstraction, to supply what isn’t already available in the C standard library, focusing on the game developer. The SDL library makes use of an event system to handle communication between the process and the external world (keyboard, mouse, windows…), which is usually used in some loop that the programmer prepares. This same structure is used in other places, including Allegro, which was the biggest competitor of SDL in the past.

The idea is to have a set of functions that make the bridge of communication between the process and the external world. In the SDL world, events are described through the non-extensible SDL_Event type. Then you use functions like SDL_PollEvent to receive events and dedicated functions to initiate operations that act on the external world. The support for asynchronous programming in the SDL library is weak, but this same event loop principle could be used in a library that would provide stronger support for asynchronous programming. Below you can find a sample that makes use of the SDL events:

There are the GUI libraries like GTK+, EFL and Qt which take this idea one step further, abstracting into an object the event loop that were previously written and rewritten by you. The Boost.Asio library, which focuses on asynchronous operations and not on GUIs, has a class of similar purpose, the io_service class.

To remove your need to write boilerplate code to route events to specific actions, the previously mentioned classes will handle this task for you, possibly using callbacks, which are an old abstraction. The idea is that you should bind events to functions that might be interested in handling those events. Now the logic to route these events belong to the “event loop object” instead a “real”/raw event loop. This style of asynchronous programming is a passive style, because you only register the callbacks and transfer the control to the framework.

Now that we’re one level of abstraction higher than event loops, let’s stop the discussion about event loops. And these objects that we referring to as “event loop objects” will be mentioned as executors from now on.

The executor

The executor is an object that can execute encapsulated units of work. Using only C++11, we can implement an executor that schedules operations related to waiting some duration of time. There is the global resource RTC and, instead of approaching the problem by creating several threads that start blocking operations like the sleep_for operation, we’ll use an executor. The executor will schedule and manage all events that are required. You can find a simple implementation for such executor following:

This code reminds me of sleepsort.

In the example, without using threads, it was possible to execute the several concurrent jobs related to waiting time. To do such thing, we gave the executor the responsibility to share the RTC global resource. Because the CPU is faster than the requested tasks, only one thread was enough and, even so, there was a period of time for which the CPU was idle.

There are some concepts to be extracted from this example. First, let’s consider that the executor is an standard abstraction, provided in some interoperable way among all the code pieces that makes use of asynchronous operations. When a program wants to do the wait asynchronous operation, the program request the operation start to some abstraction — in this case it’s the executor itself, but it’s more common to find these operations into “I/O objects” — through a function. The control is passed to the executor — through the method run — which will check for notification of finished tasks. When there are no more tasks on the queue, the executor will give back the control of the thread.

Because there is only one thread of execution, but several tasks to execute, we have the resource sharing problem. In this case, the resource to be shared is the CPU/thread itself. The control should go and come between some abstraction and the user code. This is the core of cooperative multitasking. There are customization points to affect behaviour among the algorithms that execute the tasks, making them give CPU time to the execution of other tasks.

One advantage of the cooperative multitasking style of multitasking is that all “switch” points are well defined. You know exactly when the control goes from one task to another. So that context switch overhead that we saw earlier don’t exist — where all register values need to be saved… A solution that is more elegant, efficient and green.

The object that we passed as the last argument to the function add_sleep_for_callback is a callback — also know as completion handler. Think about what would happen if a new wait operation was requested within one of the completion handlers that we registered. There is an improved version of the previous executor following:

This implementation detail reminds me of the SHLVL SHELL variable.

An interesting case is the case from the JavaScript language. JavaScript has a kind of “implicit executor”, which is triggered when the VM reaches the end of your code. In this case, you don’t need to write codes like “while (true) executor.run_one()” or “executor.run()“. You only would need to register the callbacks and make sure there are no infinite loops until the executor gets the chance to be in control.

With the motivation introduced, the text started to decrease the use of mentions to I/O for reasons of simplicity and focus, but keep in mind that we use asynchronous operation mostly to interact with the external world. Therefore many operation are scheduled conditionally in response to the notification of the completion of a previous task (e.g. if protocol invalid, close socket else schedule another read operation). Proposals like N3785 and N4046 define executors also to schedule thread pools, not only timeouts within a thread. Lastly it’s possible to implement executors that schedule I/O operations within the same thread.

Asynchronous algorithms represented in synchronous manners

The problem with the callback approach is that we no longer have a code that is clean and readable. Previously, the code could be read sequentially because this is what the code was, a sequence of instructions. However, now we need to spread the logic among lots of lots of callbacks. Now you have blocks of code that are related far apart from each other. Lambdas can help a little, but it’s not enough. The problem is know as callback/nesting hell and it’s similar to the spaghetti code problem. Not being bad enough, the execution flow became controverted because the asynchronous nature of the operations itself and constructs like branching and repetition control structures and even error handling assume a representation that are far from ideal, obscures and difficult to read.

One abstraction of procedures very important to asynchronous programming is coroutines. There is the procedure abstraction that we refer to under the name of “function”. This so called “function” models the concept of subroutine. And we have the coroutine, which is a generalization of a subroutine. The coroutine has two more operations than subroutine, suspend and resume.

When your “function” is a coroutine — possible when the language provides support to coroutines — it’s possible to suspend the function before it reaches the end of execution, possibly giving a value during the suspend event. One example where coroutines are useful is on a hypothetical fibonacci generating function and a program that uses this function to print the first 10 numbers of this infinite sequence. The following Python code demonstrate an implementation of such example, where you can get an insight of the elegance, readability and and reuse that the concept of coroutines allows when we have the problem of cooperative multitasking:

This code reminds me of the setjmp/longjmp functions.

One characteristic that we must give attention in case you aren’t familiarized with the concept is that the values of the local variables are preserved among the several “calls”. More accurately, when the function is resumed, it has the same execution stack that it had when it was suspended. This is a “full” implementation of the coroutine concept — sometimes mentioned as stackful coroutine. There are also stackless coroutines where only the “line under execution” is remembered/restored.

The N4286 proposal introduces a new keyword, await, to identify a suspension point for a coroutine. Making use of such functionality, we can construct the following example, which elegantly defines an asynchronous algorithm described in a very much “synchronous manner”. It also makes use of the several language constructs that we’re used to — branching, repetition and others:

Coroutines solve the complexity problem that asynchronous algorithms demand. However, there are several coroutines proposals and none of them was standardized yet. An interesting case is the Asio library that implemented a mechanism similar to Duff’s Device using macros to provide stackless coroutines. For C++, I hope that the committee continue to follow the “you only pay for what you use” principle and that we get implementations with high performance.

While you wait for the standardization, we can opt for library-level solutions. If the language is low-level and gives the programmer control enough, these solutions will exist — even if it’s going to be not portable. C++ has fibers and Rust has mioco.

Another option to use while you wait for coroutines it not to use them. It’s still possible to achieve high performance implementation without them. The big problem will be the highly convoluted control flow you might get.

Completion tokens

While there is no standardized approach for asynchronous operations in the C++ language, the Boost.Asio library, since the version 1.54, adopted an interesting concept. The Boost.Asio implemented an extensible solution. Such solution is well documented in the N4045 proposal and you’ll only find a summary here.

The proposal is assumes that the callback model is not always interesting and can be even confusing sometimes. So it should be evolved to support other models. Now, instead receiving a completion handler (the callback), the functions should receive a completion token, which adds the necessary customization point to support other asynchronous models.

The N4045 document uses a top-down approach, first showing how the proposal is used to then proceed to low-level implementation details. You can find a sample code from the document following:

In the code that you just saw, every time the variable yield is passed to some asynchronous operation (e.g. open and read), the function is suspended until the operation is completed. When the operation completes, the function is resumed in the point where it was suspended and the function that started the asynchronous operation returns the result of the operation. The Fiber library, shortly mentioned previously, provides a yield_context to the Asio extensible model, the boost::fibers::asio::yield. It’s asynchronous code written in a synchronous manner. However, an extensible model is adopted because we don’t know which model will be the standard for asynchronous operations and therefore we cannot force a single model to rule them all.

To build an extensible model, the return type of the function needs to be deduced (using the token) and the value of the return also needs to be deduced (also using the token). The return type is deduced using the token type and the returned value is created using the token passed as argument. And you still have the handler, which must be called when the operation completes. The handler is extracted from the token. The completion tokens model makes use of type traits to extract all information. If the traits aren’t specialized, the default behaviour is to treat the token as a handler, turning the approach compatible with the callback model.

Several examples are given in the N4045 document:

  • use_future
  • boost::fibers::asio::use_future
  • boost::fibers::asio::yield
  • block

The std::future approach have meaningful impact on performance and this is not cool, like explained in the N4045 document. This is the reason why I don’t mention it in this text.

Signals and slots

One alternative that was proposed to the model of callbacks is the signals and slots approach. This approach is implemented in libgsigc++, Boost, Qt and a few other libraries.

This proposal introduces the concept of a signal, which is used to notify an event, but abstracts the delivery of the notification and the process of registering functions that are interested in the event. The code that notifies events just need to worry about emitting the signal every time the event happens because the signal itself will take care of handling the set of registered slots and stuff.

This approach usually allows a very decoupled architecture, in opposition of the very verbose approach largely used in Java. An interesting effect, depending on the implementation, is the possibility to connect one signal to another signal. It’s also possible to have multiple signals connected to one slot or one signal connected to multiple slots.

The signal usually is related to one object, and when the object is destroyed, the connections are destroyed too. Just like it’s also possible to have slot objects that automatically disconnect from connected signals once destroyed, so you have a safer abstraction.

Given signals are independently implemented abstractions and usable as soon as they are exposed, it’s naturally intuitive to remove the callback argument from the operations that initiate asynchronous operations to avoid duplication of efforts. If you go further in this direction, you’ll even remove the own function to do asynchronous operations, exposing just the signal used to receive notifications, your framework you’ll be following the passive style instead active style. Examples of such style are the Qt’s socket, which doesn’t have an explicit function to request the start of the read operation, and the POCO library, which doesn’t have a function to request that receiving of a HTTP request.

Another detail which we have in signals and slots approach is the idea of access control. In the Qt case, signals are implemented in a way that demands the cooperation of one preprocessor, the own Qt executor and the QObject class. In the Qt case, the control access rules for emitting a signal follow the same rules for protected methods from C++ (i.e. all children classes can emit signals defined in parent classes). The operation of connecting a signal to another signal or a slot follows the same rules of public members of C++ (i.e. anyone can execute the operation).

In the case of libraries that implement the signal concept as a type, it’s common to observe a type that encapsulate both, the operation to emit the signal and the operation to connect the signal to some slot (different from what we see in the futures and promises proposal, where each one can have different control access).

The signals and slots approach is cool, but it doesn’t solve the problem of complexity that is solved with coroutines. I only mentioned this approach to discuss better the difference between the active style and the passive style.

Active model vs passive model

In the passive model, you don’t schedule the start of the operations. It’s what we commonly find in “productive” frameworks, but there are many questions that this style doesn’t answer quite well.

Making a quick comparison between the libraries Qt and Boost.Asio. In both libraries, you find classes to abstract the socket concept, but using Qt you handle the event readyRead using the readAll method to receive the buffer with the data. In contrast, using Boost.Asio, you start the operation async_read_some and pass the buffer as argument. Qt uses the passive style and Boost.Asio uses the active style.

The readyRead event, from Qt, acts independently from the user and requires a buffer allocation every time it occurs. Then some questions arise. “How can I customize the buffer allocation algorithm?”, “how can I customize the application to allow recycling buffers?”, “how do I use a buffer that is allocated on the stack?” and more. The passive model doesn’t answer questions like these, then you need to fatten the socket abstraction with more customization points to allow behaviours like the ones described. It’s a combinatorial explosion to every abstraction that deals with asynchronous operations. In the active model, these customizations are very natural. If there is any resource demanded by the operation that the developer is about to start, the developer just need to pass the resource as an argument. And it’s not only about resource acquisition. Another example of question that the passive model doesn’t answer well is “how do I decide whether I’m gonna accept a new connection or postpone to when the server is less busy?”. It’s a great power to applications that are seriously concerned about performance and need fine adjustments.

Besides performance and fine tuning, the passive model is also causes trouble to debugging and tests thanks to the inversion of control.

I must admit that the passive model is quite good to rapid prototyping and increasing productive. Fortunately, we can implement the passive model on top of the active model, but the opposite is not so easy to do.

Bonus references

If you like this subject and want to dig more, I suggest the following references:

Boost.Http rejected

Previously I informed that my library was about to be reviewed by the Boost community. And some time ago, this review happened and a result was published.

The outcome of the Boost review process was that my library should not be included in Boost. I think I reacted to the review very well and that I defended the library design with good arguments.

There was valuable feedback that I gained through the review process. Feedback that I can use to improve the library. And improvements to the library shouldn’t stop once the library is accepted into Boost, so I was expecting to spend more time in the library even after the review, as suggested by the presence of a roadmap chapter on the library documentation.

The biggest complaint about the library now was completeness. The library “hasn’t proven” that the API is right. The lack of higher-level building blocks was important to contribute to the lack of trust in current API. Also, if such library was to enter in Boost, it should be complete, so new users would have a satisfying first impression and continue to use the library after the initial contact. I was worried about delivering a megazord library to be reviewed in just one review, but that’s what will happen next time I submit the library to review. At least I introduced several concepts to the readers already.

Things that I planned were forgotten when I created the documentation and I’ll have to improve documentation once again to ensure guarantees that I had planned already. Also, some neat ideas were given to improve library design and further documentation updates will be required. Documentation was also lacking in the area of tutorial. Truth be told, I’m not very skilled in writing tutorials. Hopefully, the higher-level API will help me to introduce the library to newbies. Also, I can include several tutorials in the library to improve its status.

There was an idea about parser/generator (like 3-level instead 2-level indirection) idea that will require me to think even more about the design. Even now, I haven’t thought enough about this design yet. One thing for sure is that I’ll have to expose an HTTP parser because that’s the only thing that matters for some users.

A few other minor complaints were raised that can be addressed easily.

If you are willing to discuss more about the library, I have recently created a gitter channel where discussions can happen.

And for now, I need to re-read all messages given for the review and register associated issues in the project’s issue tracker. I’d like to have something ready by January, but it’ll probably take 6 months to 1 year before I submit the library again. Also, the HTTP client library is something that will possibly delay the library a lot, as I’ll research power users like Firefox and Chromium to make sure that the library is feature-ready for everybody.

So much work that maybe I’ll submit the library as a project on GSoC again next year to gather some more funding.

Also, I’d like to use this space to spread two efforts that I intend to make once the library is accepted into Boost:

  • A Rust “port” of the library. Actually, it won’t be an 1:1 port, as I intend to use Rust’s unique expressiveness to develop a library that feels like a library that was born with Rust in mind.
  • An enhanced, non-slow and not resource-hungry implementation (maybe Rust, maybe C++) of the trsst project.


Faz algum tempo desde a última vez que escrevo sobre algum padrão de projeto. O último texto que lembro foi o texto sobre CRTP, e nesse tempo meu conhecimento sobre programação aumentou, minhas habilidades comunicativas aumentaram e eu passei a escrever textos melhores. O texto que fiz sobre CRTP, nem acho importante. Entretanto, decidi fazer um texto para explicar monads, pois existe toda essa tradição de que, ao compreender o que é monad, você atinge uma epifania satisfatoriamente envolvente, você sente um desejo incontrolável de tentar compartilhar esse conhecimento com toda a humanidade, e você FALHA, mas, até pior que isso, seu manual é diferente e as pessoas vão perder até mais tempo tentando aprender o que é monad, pois agora há até mais textos confusos que acumulamos.

Eu, é claro, demorei muito tempo para aprender monads, apesar de estar usando monads há bastante tempo. Quando finalmente aprendi o que é monad, percebi o quão simples é esse conceito. Devo essa compreensão ao Douglas Crockford.


All told, a monad in X is just a monoid in the category of endofunctors of X, with product × replaced by composition of endofunctors and unit set by the identity endofunctor.

Entendeu? Eu também não, e esse tipo de explicação era uma das razões para eu ter demorado a aprender.

Uma coisa importante para prosseguir com a explicação, é deixar claro os termos utilizados. E o primeiro termo que pretendo deixar claro é o de valor. Em programação funcional pura, não é tão raro assim evitar o uso do termo variável e usar, no lugar, o termo associar (binding em inglês), para indicar que um nome está se referindo a algum valor. Quando você se refere a variável, talvez implicitamente você assuma que você pode mudar o valor dessa variável, mas nem sempre é essa a mensagem que queremos comunicar. Há também o termo objeto, do paradigma de programação orientada a objetos, que podemos usar no lugar do termo variável, mas o termo objeto também carrega  outras informações que podem não ser necessárias para a explicação que estamos tentando passar. Por essas razões, eu vou, durante o texto, tentar usar o termo valor, mas se você substituir esse termo por objeto ou variável, ainda é provável que o texto continue fazendo sentido.

Há também o termo função, que em programação pode acabar tendo um significado diferente. Em programação, há o termo subrotina, que não equivale ao termo de função na matemática. Entretanto, algumas linguagens de programação usam o termo função para se referir ao conceito de subrotina, criando uma situação de falso cognato. Para “diminuir” a confusão, passamos a chamar de funções puras, as funções que possuíam o mesmo significado que as funções possuem em matemática. Essa curiosidade não é tão importante para o entendimento de monads, mas é bom que você comece a consumir algumas informações relacionadas para estar estimulado o suficiente quando a explicação de monads aparecer.

Ainda no tópico de funções, temos a diferença de funções membros e funções não-membros. Funções membros são funções que recebem o this/self, que fazem parte de alguma classe e não são métodos estáticos, funções que chamamos de métodos na programação orientada a objetos. Funções não-membros são funções livres, que não fazem parte de nenhuma classe. Quando eu me referir a uma função que recebe a e b como argumentos, as duas situações exemplificadas no seguinte código são interpretações válidas:

Nos dois casos, temos uma função foo que recebe a e b como argumentos. Há até esforços para tornar a sintaxe de chamada de funções em C++ mais uniforme. A linguagem Rust está ligeiramente à frente nessa corrida, enquanto outras linguagens já chegaram lá.

Você pode enxergar monad como um padrão de projeto, e, dentro desse padrão de projeto, há o “objeto” que de fato representa o monad. Assim como ocorre no padrão Singleton, onde o mesmo termo, Singleton, se refere (1) ao padrão de projetos e a (1) um objeto que respeita algumas características. Estou citando o padrão Singleton, porque esse é, pela minha experiência, o padrão mais famoso e mais fácil de entender. A parte importante para manter em mente é que monad é um valor, mas, em alguns momentos, pode ser que eu me refira a monad como um padrão de projeto.

Valores embrulhados

Em alguns cantos na terra da programação, podemos encontrar valores que são embrulhados. Por exemplo, temos a classe Integer, na linguagem Java, que embrulha um valor primitivo int. Temos também, em C++, a classe auto_ptr<T>, que embrulha um valor do tipo T. Instâncias dessas classes não são monads.

Valores embrulhados por outro valor são isolados, tornando-se acessíveis somente a partir de uma interface. Eles não podem ser acessados diretamente, pois estão embrulhados em outro valor, seja lá qual for o propósito. No caso da classe Integer, você precisa usar a função intValue para acessar o valor embrulhado. No caso da classe auto_ptr<T>, você precisa usar a função que sobrecarrega o operador de desreferenciamento da classe.

Monads também embrulham valores. Monads são valores que embrulham outros valores e apresentam três propriedades. Entretanto, eu só vou informá-las ao final do texto.

Função unit e função bind

Unit é uma função que recebe como argumento o valor a ser embrulhado e retorna o monad. É também conhecido como construtor em outros locais. Seria o “construtor” do monad.

Bind é uma função que recebe dois argumentos, um monad e uma função que tenha a “mesma assinatura” de unit, e retorna um novo monad. Usei a expressão “mesma assinatura” entre aspas, pois, na verdade, a situação não precisa ser tão estrita assim. O tipo de retorno pode ser um monad diferente, mas o argumento de entrada precisa ser do mesmo tipo ou compatível.

Em um monad, deve existir uma forma de acessar o valor embrulhado através da função bind, mesmo que essa não seja a única forma de acessar o valor, e mesmo que a função receba outro nome que não seja bind. Daí que encontramos algumas discussões usando termos como “monadic operations“.

A ideia por trás dessa estrutura é a componibilidade. Talvez não seja tão incrível para eu ou você, pois estamos acostumados a ter outras ferramentas disponíveis, mas parece ser um conceito incrível para os programadores da comunidade de programação funcional pura.

Em Haskell, monads costumam ser usados para isolar comportamentos que não são livres de efeitos colaterais, como I/O. Rust, uma linguagem que possui “;”, não precisa de monads, mas o núcleo de seu sistema da tratamentos de erros é feito em cima das ideias de monads.

Alguns monads


Maybe é um monad, em alguns locais recebendo o nome de Option ou optional, e às vezes nem sequer obedecendo as propriedades de um monad. O propósito de Maybe é simples: possivelmente armazenar um valor. Você pode encarar esse monad como uma abstração que lhe permite expressar a semântica de valores opcionais, que em C são representados através de ponteiros que possivelmente possuem o valor NULL, ou em Java, onde qualquer objeto pode ser null.

O legal desse monad é que você ganha segurança, pois verificar pela existência do valor para então acessá-lo não acontece. É mais seguro, porque, no modelo antigo, era possível você tentar acessar o valor esquecendo de verificar antes se ele existia, esquecer do if. Com o padrão de monad/Maybe, você só usa a função bind que já recebe o valor “extraído” e ela só é chamada se o valor for diferente de None.

A verdade é que esse monad fica bem mais legal se houver suporte a nível de linguagem para exhaustive pattern matching, como ocorre em Rust. Em Rust, a abstração que equivale a esse monad é a abstração Option.

Na verdade, o exemplo de código anterior usa um bind “não-conformante”, pois a função/closure passada como argumento não retorna outro monad, mas escrevi mesmo assim para ser breve. Pattern matching costuma ser mais interessante para acessar o valor e você abstrai outros comportamentos que são mais interessantes para serem encapsulados como “operações monádicas” (a documentação da abstração Option em Rust é um ótimo exemplo).


Se Maybe é a abstração para valor-ou-nada, Result é a abstração para valor-ou-erro. Suponha uma função que converta uma string para um inteiro de 32 bits. Erros podem acontecer e pode ser que você queira usar o Maybe<int> como retorno da função, mas talvez seja interessante diferenciar o porquê da função ter falhado, se foi devido a uma string inválida ou overflow, por exemplo.

Result como monad é bem interessante, porque lhe permite encadear uma cadeia de ações e, caso alguma delas retorne erro, a cadeia para, e o erro é armazenado, sem que você precise explicitamente verificar por sua existência. Na linguagem Rust, até existe uma macro, try!, que torna o código até mais legível a agradável.

É interessante ter operações monádicas em Result que só atuam em um dos valores (o esperado ou o valor de erro), então você teria Result<T, E>::map e Result<T, E>::map_err.


Futures e promises se referem a um padrão na computação para abordar o problema de concorrência e dependência de dados. Nesse padrão, o future é um valor que embrulha o resultado, que é computado assincronamente. Alguma função como get pode ser usada para obter o resultado e, caso não esteja pronto, a thread atual é bloqueada até que ele esteja disponível.

A adição de operações monádicas permite definir uma API que anexe continuações que seriam executadas quando o resultado estiver pronto, livrando-lhe do problema de bloquear a thread atual (que essencialmente mata o paralelismo). C++ é uma linguagem que atualmente possui abstrações de futures e promises, mas não possui as operações monádicas que tornariam essa abstração muito mais agradável de ser utilizada, como é explicado em um bom texto do Bartosz Milewski.

Programadores de NodeJS querem usar futures para se livrar de códigos profundamente aninhados (também conhecido como nesting hell). Futures são monads que podem livrar o programador da verificação explícita de erros para cada operação. Se bind for uma função-membro, o aninhamento infernal é, de fato, eliminado. O benefício é um ganho na legibilidade que pode ter grandes consequências como produtividade aumentada e diminuição de erros.

As três propriedades de um monad

A terceira propriedade existe para garantir ordem. E isso é importante para composibilidade.


Utilidade. A utilidade de você entender monads é que agora, quando os programadores de Haskell invadiram sua comunidade de programação, você vai entender o que eles querem dizer por monad. A utilidade se resume a comunicação, mas eu não recomendo que você use o termo monad para documentar abstrações que você crie que modelem o conceito de monad, pois é inútil. Dizer que uma abstração modela o conceito de monad não vai me fazer querer usar tal abstração. Demonstrar a utilidade da abstração vai me fazer querer usá-la e omitir o termo monad não vai tornar a demonstração mais longa ou menos clara. Só use o termo monad para questão de comunicação, quando você estiver tentando se comunicar com programadores de Haskell.

Boost.Http scheduled for review

February last year, I was writing an email to the Boost user mailing list, asking for feedback on an HTTP server library proposal. Later that year, I got the news that I was to receive funds through Google to work on such proposal. And I had delivered a library by the asked timeframe. However, getting the library into Boost is another game. You must pass through the review process if you want to integrate some library into Boost. And I’d like to announce that my library is finally going to be reviewed starting on Friday.

Some links:

After working on this project for more than a year, I’m pretty glad it finally reached this milestone. And I’m very confident about the review.

I had written about this project so much here and there that when the time to write about it in my own blog comes, I don’t have many words left. It’s a good thing to leave as much info on the documentation itself and don’t spread info everywhere or try to lure users into my blog by providing little information on the documentation.

No tópico de Garbage Collector

Você quer que sua linguagem tenha Garbage Collector. Isso é uma mentira.

Você quer que sua linguagem tenha gerenciamento automático de memória. Isso ainda é uma mentira, mas pelo menos é uma mentira menor.

Você quer que sua linguagem tenha gerenciamento automático de recursos, não importando se eles são memória, file descriptors, socket descriptors ou qualquer outro. Isso NÃO é mentira.

Gerenciamento de memória (e outros recursos) seguro, automático, barato, performático e sem Garbage Collector é possível, como a linguagem Rust vem provando. Rust é a linguagem formidável que realmente se preocupa com performance e é capaz até de detectar data race em tempo de compilação.

Problemas de usabilidade com Garbage Collector

Garbage Collectors não possuem semânticas fortes o suficiente para permitir destrutores. O mais próximo que existe são os finalizers, que possuem uma lista bem grande de possíveis problemas.

É justamente devido ao Garbage Collector e aos finalizers que você está tendo mais trabalho, tendo que gerenciar blocos finally após cada tratemento de exceção. Na verdade, eu também não acredito em tratamento de erros usando exceções (leia o link para não entender errado!).

Outros problemas com Garbage Collector

O uso de um Garbage Collector diminui a utilidade do seu programa. Se seu programa possuir requisitos de tempo real, baixa latência, sistemas embarcados, uso eficiente de bateria ou alguns outros relacionados, Garbage Collector se torna indesejável.

Uma crítica inicial a Garbage Collector é que seu programa congela aleatoriamente, prejudicando a responsividade, mas esse problema é até mais prejudicial no caso do programa ter requisitos de tempo real, pois nenhuma garantia de tempo máximo gasto com coleta de lixo é oferecida. Foram então, criados algoritmos de GC que rodam em paralelo e algoritmos incrementais de GC, permitindo o uso de GC nesses cenários, mas outros problemas persistem. Primeiro, que uma solução dessas continua a consumir mais recursos.

Outra crítica a GC é que ele é um consumidor exagerado de recursos. Se o algoritmo é paralelo, você já depende de uma segunda thread. A depender do algoritmo adotado, você já vai precisar do dobro de memória RAM que uma versão do programa sem GC. E o programa estará usando mais memória que o que precisa na maior parte do tempo, então não podemos chamá-lo de “leve”. Tudo isso contribuindo para o mal uso da bateria do dispositivo.

Mais uma crítica ao GC é que, quando ele executa, vai caminhar aleatoriamente na RAM, navegando de ponteiro em ponteiro, poluindo a cache da CPU e impedindo que você crie um programa de baixa latência, onde responsividade é importante (i.e. jogos).

Um tweeteiro aleatório me deixou ciente de um diagrama legal que pode facilmente lhe fazer pensar que talvez “C++ sempre vença no desafio de performance”. Acho tal gráfico relevante para o tema:


Eu não tenho nada para concluir de verdade. Só criei essa seção para ter a oportunidade de usar uma frase: C++ foi minha escola.


É legal ver a cara de “WTF” do Alexandrescu ao escutar o Stephan T. Lavavej falar “I don’t think garbage collection can ever solve it”.

Tratamento de erros usando exceções sob a perspectiva de um programador de C++

Um tópico sobre o qual sua compreensão pode evoluir muito com o tempo, pois pouca experiência tem uma grande contribuição para impedir que os conceitos se tornem claros é o tópico de tratamento de erros usando exceções. Em C++, esse é um assunto até mais polêmico que em muitas outras linguagens e eu resolvi compartilhar um pouco de minha compreensão nesse texto.

Eu quero que você se esforce muito para não pensar nas implicações de performance que exceções introduzem no sistema, pelo menos até terminarmos de analisar as outras facetas dessa ideia, pois há muitos argumentos irracionais, que, na minha experiência, vêm principalmente de pessoas que estão falsamente preocupadas com performance. Se esforce para manter em mente as frases “um erro é uma exceção?” e “o que é um caso excepcional?”. Elas vão ajudar a entender motivações e serão discutidas mais adiante no texto.

Exceções – resumo/revisão rápida

Exceções formam um conceito de programação idealizado para tornar difícil o ato de ignorar erros, separar código do fluxo normal do programa do código de tratamento de erros — aumentando a legibilidade, diminuindo erros e melhorando a manutenção do sistema — e transportar tanta informação sobre o erro quanto possível.

A motivação para exceções terem sido idealizadas é inspiradora, principalmente quando se pensa em estilos de notificações de erros alternativos que eram/são comuns, como variáveis globais (errno), valores de retorno onde alguns valores possíveis são reservados para indicar erro (normalmente -1), valores de retorno representando um código de erro, valores de retorno indicando sucesso ou erro e outros. Algumas dessas técnicas alternativas poluem abstrações, como no caso onde o valor de retorno da função torna-se reservado para o erro e a função precisa ser refatorada para enviar o resultado através dos argumentos passados para ela.

Algumas das técnicas alternativas evoluíram para soluções que chegaram a adquirir o meu selo de “minimamente respeitável”, e agora nós temos outras soluções para se levar a sério, como, por exemplo, verificações forçadas estaticamente através do sistema de tipos (veja o exemplo de Rust). Mas, esse texto é sobre exceções, então, a partir de agora, evitarei menções desnecessárias a essas abordagens alternativas.

Uma proposta de “expected” para C++ sugere quatro características importantes para um sistema de tratamento de erros:

  • Visibilidade do erro.
  • Informação nos erros.
  • Código limpo.
  • Erros não intrusivos.

Eu recomendo a leitura da parte inicial da proposta para entender essas quatro características. Elas ajudam a julgar o que é um bom sistema de tratamento de erros, ressaltando indicativos importantes.

Isso é introdução mais que o suficiente para se começar a descrever o que são e como funcionam exceções. Exceções definem construções para o controle de fluxo de execução do programa onde separamos código “normal” de código “excepcional”. E existe a exceção, usada para representar e transportar informações sobre um erro. Quando um erro é detectado e a exceção correspondente é gerada, o fluxo de execução normal é abortado e o fluxo de execução excepcional é iniciado, recebendo a exceção lançada para que se possa fazer a decisão de tratamento de erro apropriada.

Sintaticamente, envolvemos o fluxo de código normal para o qual somos capazes de tratar alguma exceção usando um bloco “try” e, logo em seguida, introduzimos um bloco “catch”, que contém o código para o fluxo excepcional. Um exemplo é dado abaixo:

Bem simples e, para muitos novatos, epifanicamente elegante.

Onde exceções decepcionam

O principal problema de usabilidade com exceções, tal como é implementado na linguagem C++, é que você nunca está certo de que está tratando todas as exceções possíveis. Você chama uma função, verifica a assinatura dela, e não sabe quais possíveis exceções serão lançadas. E se, após uma atualização, a função passar a lançar um novo tipo de exceção, todo o código que a chama vai continuar compilando normalmente, sem nem mesmo um aviso sobre a necessidade de atualizar o código chamador, mas causará um erro em tempo de execução nos piores momentos — que são os momentos quando o programador está confiante que o sistema está pronto para produção.

Em Java, há uma funcionalidade chamada de exceções verificadas, em que as possíveis exceções a serem lançadas por uma função fazem parte de sua assinatura. Ou seja, o sistema de exceções é integrado ao sistema de tipos para aumentar a segurança do sistema. Com tal funcionalidade, se, para uma dada função, não há a declaração de que uma exceção X é possivelmente lançada, e essa possibilidade existe no corpo da função, um erro de compilação é gerado. Ou você trata toda as exceções que podem ser lançadas dentro da própria função, ou informa ao código chamador que é possível que algumas exceções sejam lançadas.

Há quem considere catch(…) com rethrow um devido tratamento de exceção, mas a exceção não está sendo tratada. Você está meramente ignorando toda a informação sobre o erro e transferindo a responsabilidade de tratá-la para algum outro lugar. Esse padrão é útil, no entanto, para fornecer garantias de exceções básicas e fortes, dois conceitos presentes na STL.

Não é possível deferir o tratamento de exceções, situação que já podemos encontrar em casos apenas um pouco mais distantes que os imaginados e idealizados. Em programação concorrente (assíncrona ou paralela), o ponto onde uma operação é iniciada e onde é completada podem ser diferentes. Tais pontos sendo diferentes, a exceção não é retornada para o código que requisitou a realização da operação, e você simplesmente usa outro paradigma para tratamento de erros. É até mais complicado quando se envolve threads — problema para o qual o padrão C++11 adicionou exception_ptr. Um dos paradigmas, o paradigma de futures & promises, restaura o suporte a exceções nesses casos, fazendo com que a operação de desembrulhar o valor, através da função-mebro get, possivelmente lance uma exceção, mas, para funcionar, esse truque se apoia no fato de que as exceções em C++ não são verificadas.

Um ponto final sobre a complexidade que exceções introduzem está relacionada a código de finalização, que sempre precisa ser chamado, independente de uma exceção ter sido lançada ou não. Se a linguagem não suporta RAII, adiciona blocos “finally” para resolver o problema. Se a linguagem suporta RAII, um conjunto de convenções e regras é criado e a possibilidade de se lançar uma exceção enquanto outra já está sendo lançada nasce, o que normalmente provoca o programa a ser terminado.

Basicamente, exceções podem se tornar o novo goto e, elas são até abusadas, para, por exemplo, se implementar sistema de pattern matching em C++.


Finalmente, o tópico que causa polêmica e que desencoraja o uso de exceções em qualquer caso que não seja excepcional, mas cuja preocupação ajuda a definir o que é um caso excepcional.

A primeira observação a se fazer sobre exceções é que elas não são lentas. Citando outro texto de minha própria autoria:

O documento TR18015 detalha duas abordagens principais para a implementação de suporte a exceções e o que o documento menciona como “abordagem tabela” é muito barata, realizando boa parte do que precisa ser computado em tempo de compilação e estruturando o código de forma que não há penalidade em tempo de execução para o fluxo de execução normal do programa. O custo, nessa abordagem, é que o programa fica maior, mas a parte extra pode ser movida para não interferir negativamente no uso de cache e, em sistemas com suporte a memória virtual, essa parte pode até ficar na swap até que torne-se necessária.

uma resposta no StackOverflow, caso você não queira se dar ao trabalho de passar pelo TR18015. A implementação do suporte a exceções é bem elegante e a discussão deveria deixar de ser se exceções são lentas e passar a ser quando usar exceções.

E cuidado com os benchmarks injustos que alimentem esse mito, assim como o P. alerta.

Então usar exceções pode conduzir a um código até mais rápido do que manualmente verificar se erros ocorreram, mas caso um erro aconteça, haverá uma drástica redução na performance. E, como exceções dependem do mecanismo de RTTI, pode ser que tratar erros usando exceções deixe de ser uma opção em sistemas de tempo real.

A solução para esse problema está em colocar uma linha divisória entre erros e exceções. Ou, mais objetivamente, o que é um caso excepcional. Se você usar exceções apenas em casos excepcionais, não há problema nenhum em utilizá-las devido a alguma degradação de performance, pois você atingiu um caso excepcional de qualquer forma.

Um exemplo de um caso excepcional é quando o seu sistema encontra um erro de lógica, pois continuar executando o programa pode causar corrupção do seu estado interno e ele deveria ser encerrado o mais cedo possível, parando somente para mostrar uma mensagem de erro ao usuário.

Outro exemplo de caso excepcional é um cliente de um jogo online, onde, caso a conexão caia, você não se importa com o tempo gasto para lançar a exceção, pois o sistema não pode fazer nada de útil enquanto o erro não for tratado, e reconectar ao servidor é uma operação que vai custar mais tempo que o tratamento da exceção, de qualquer forma. Esse mesmo exemplo de “queda de conexão”, entretanto, pode virar um caso não excepcional em um contexto diferente. No contexto de um servidor que está servindo milhares de clientes, por exemplo, quedas de conexões são esperadas o tempo todo e você não quer que o gargalo de lançar milhares de exceções diminua a capacidade do seu servidor.

O usuário do reddit @sazzer ilustra mais alguns casos do que é uma situação excepcional.


Responder a pergunta “esse é um caso excepcional?” é uma tarefa complicada, mas, pode se tornar mais fácil, fazendo uso de bom senso, das ferramentas certas e de um bom entendimento de suas implicações.

Além disso, um mesmo erro pode ser excepcional ou não a depender do contexto, então é prefirível que se evite a dependência em exceções.

Aliado a todos os problemas que exceções trazem, eu sugiro que seu uso, em C++, seja reduzido. Entretanto, você simplesmente não pode se livrar de exceções usando C++ idiomático, então compreendê-las é importante.

Um sistema de tratamento de erros unificado é atraente, pois elimina a chance de escolhermos a abordagem errada em algum ponto. Entretanto, um sistema de tratamento de erros unificado deveria ter todas as vantagens que exceções possuem e se livrar de seus problemas mais sérios, inclusive os problemas de performance que impossibilita seu uso em sistemas de tempo real.

Pessoalmente, o sistema de tratamento de erros que eu vejo como ideal é a abordagem que é adotada em Rust, que emprega uso de conceitos como algebraic data types e pattern matching para oferecer um sistema de tratamento de erros bem expressivo e que é difícil de usar erroneamente.

Representação do mundo de jogos Bomberman-like

Existem aqueles problemas não-imediatos como desempenho, elegância ou outros que apenas ficam no caminho para a primeira solução funcional. Há aqueles que condenam a prática de preocupação prévia e insistem em resultados imediatos. Estou trabalhando no desenvolvimento de um jogo similar ao Bomberman e precisei desenvolver uma solução para representar o mapa onde o mundo do jogo é executado. E, é claro, acabei tendo o luxo de escolher a abordagem de desenvolvimento onde a preocupação com problemas não-imediatos ocorre mais cedo que a entrega do produto.


Para que o problema não fique muito abstrato, considere a imagem abaixo, retirada do jogo BlowThemAll.


O mapa do jogo possui uma implementação similar ao conceito de uma matriz, onde os elementos do mapa possuem uma posição <x, y> tal que essa posição não pode ultrapassar os limites da matriz e os índices x e y são números discretos, naturais. Cada posição no mapa é um tile e cada tile pode conter vários elementos.

O mapa contém elementos imóveis, tais como blocos a serem explodidos, e elementos móveis, tais como bombas e avatares. Avatar é um personagem que representa a conexão do jogador com o mundo virtual de BlowThemAll.

Há ainda blocos eternos, que não podem ser explodidos.

No jogo original, Bomberman, uma bomba explode e a explosão tem o alcance baseado em tiles. Itens aparecem somente em tiles específicos. Há muitas observações que sugerem fortemente o uso de uma matriz como uma representação eficiente para o jogo.


Jogando-se Bomberman é possível observar que um avatar pode estar entre dois tiles, então você não pode representá-los como um elemento em uma sequência que faz parte de algum tile, pois um avatar pode estar entre dois tiles. Logo, o avatar precisa ser independente dessa matriz e não pode ter sua posição representada por números discretos.

Abordagens iniciais

A primeira abordagem para o problema foi utilizar uma lista separada para esses elementos do mapa que podem assumir posições entre dois tiles e armazenar suas respectivas posições usando valores do tipo double (ou parecidos).

Entretanto, eu não me sinto confortável usando valores do tipo double em um jogo em rede. Primeiro, porque o comportamento de doubles não é trivial, e segundo, porque é difícil garantir reprodutibilidade/previsibilidade usando doubles, então  eu não quero lidar com o problema desses números sendo utilizados numa parte principal de um jogo em rede. Lembrando que o double que você está usando pode nem ser o IEEE floating point. É claro que eu iria ler o “what every computer scientist should know about floating point numbers” caso o projeto precisasse, mas eu queria, ao menos, considerar uma abordagem alternativa.

A minha ideia foi usar números discretos para as posições dos elementos não-fixos no mapa, e fazer numa escala diferente (se o mapa era 13×11 tiles, os elementos não-fixos estariam em uma matriz virtual 130×110) para permitir um valor mais granular (que emularia o fato do avatar poder estar entre dois tiles). Essa abordagem não me contentou, pois ela levantava outras perguntas (ex.: quão granular devo permitir a posição para permitir uma experiência de jogo agradável?), não era elegante e tirava a simplicidade que eu tinha na solução inicial, onde, era barata, a operação de encontrar quais os avatares eram atingidos por uma explosão.

Pensando nas restrições

Eu voltei ao problema inicial para pensar nas restrições que ele apresentava. A depender de suas restrições, o algoritmo poderia resolver um problema menor e mais simples. Foi essa a abordagem que me permitiu desenvolver a libdepixelize com menos código e mais rápido, e é isso que decidi também fazer no BlowThemAll, entender melhor o problema.

Daí, percebi algo enquanto jogava bomberman. O avatar pode sim, estar entre duas posições, mas caso ele esteja entre duas posições, ainda há um “tile principal” ao qual ele pertence. Isto é, ele só é atingido por uma explosão caso apenas um dos tiles, o “tile principal”, seja incendiado. Além disso, há a restrição de que ele só pode estar entre dois tiles, não entre quatro tiles. Todas essas informações me inspiraram o suficiente para pensar numa solução alternativa.

Solução final

A ideia-chave da solução que acabei adotando é armazenar os elementos na “matriz”, como estava inicialmente planejado, porém adicionar um atributo “filled”, que representava o quanto  daquele tile o elemento ocupa, e um atributo direção, que representa qual o próximo tile a ser ocupado, quando o elemento atual chegar no limite de desocupação do tile atual.

Conceitualmente, 1 representa o estado onde o elemento está ocupando somente aquele tile, 0 representa o estado onde ele não está ocupando nada daquele tile e temos também todos os valores intermediários (como ocorre em contas de probabilidade). Entretanto, queremos usar valores discretos, então usamos inteiros simples e usamos seus limites (0 e 255) para representar os limites conceituais (0 e 1).

Um elemento é transferido para o próximo tile, quando, conceitualmente, ele desocupa mais da metade do tile atual. Se mapearmos o comportamento conceitual para o comportamento em código, então haverá um desperdício de bits, pois o valor de filled nunca seria inferior a 127. Assim sendo, o valor 255 continua representando o conceitual 1, mas o valor 0 representa o conceitual 1/2.

E a solução ficou bem legal até, mas eu ainda queria mais. Uma granularidade de 256 valores possíveis parecia um desperdício para mim e eu queria usá-los para mais informações. Foi então que eu decidi separar preenchimento/ocupação/”filled” por dimensão. “dir” se transformou nos atributos “is_up” e “is_left”, enquanto “filled” se transformou nos atributos “y_filled” e “x_filled”. Usando campos de bits, consegui armazenar toda essa informação em um único byte.

Os atributos “is_up” e “is_left” podem lembrar o bit de sinal da representação de inteiros por complemento de 1, que desperdiça um valor possível, porém eu quero preservar a simetria, então esse desperdício é aceitável.

Essa separação por dimensão, entretanto, faz com que o objeto sempre assuma uma direção para cada dimensão. Se antes as opções de direção eram norte, sul, leste e oeste, agora as direções são nordeste, noroeste, sudeste, sudoeste. Essa característica não é um problema, mas é preciso manter ela em mente quando for implementar funcionalidades que dependem da direção do personagem (socos, por exemplo).

Outra consequência da separação é que o jogo volta a apresentar a possibilidade inicial onde um personagem pode assumir uma posição de estar entre quatro tiles. Essa é uma característica positiva, pois impõe menos restrições e dá mais liberdade artística aos autores de mapas. Essa possibilidade também está presente no jogo Bomberman Tournament e eu não quero fazer menos que os competidores.

E, assim, eu fiquei satisfeito, e surgiu o código do BlowThemAll para tratar mapas. É claro que eu não acho que usaria essa solução para um jogo estilo “The Legend of Zelda: A Link to the Past“, mas eu acho que ficou bem adequada a um jogo ao estilo de Bomberman. Pode ser que você ache interessante pesquisar também sobre Quadtree Collision detection.

Programação em tempo real

Caso não fosse um jogo em rede e que possui algumas características que eu não detalhei, eu estaria também mantendo a preocupação de fazer uma solução para programação em tempo real, afinal, é um jogo e queremos previsibilidade nos tempos de respostas máximos. Sem essa preocupação, a única modificação que pensei para melhorar a previsibilidade nos tempos de resposta seria usar a função vector::reserve para pré-alocar um pouco de memória para a lista de elementos em cada tile.


E para acabar o post, deixo um vídeo de alguns segundos demonstrando o resultado:

%d blogueiros gostam disto: