
Este artículo es una continuación de un artículo de reflexión publicado hace un mes, "
¿Es fácil agregar nuevas funciones al marco anterior? La agonía de elegir en el ejemplo del desarrollo de SObjectizer ". Ese artículo describió la tarea que queríamos resolver en la próxima versión de SObjectizer, examinó dos enfoques para su solución y enumeró las ventajas y desventajas de cada uno de los enfoques.
Con el paso del tiempo, se implementó uno de los enfoques y nuevas versiones de
SObjectizer , así como el
proyecto adjunto
so_5_extra , ya llamado "
respirar profundamente ". Literalmente puedes tomar y probar.
Hoy hablaremos sobre lo que se hizo, por qué se hizo, a qué condujo. Si alguien está interesado en seguir cómo se está desarrollando uno de los pocos marcos de actores en vivo, multiplataforma y abiertos para C ++, puede utilizar cat.
¿Cómo empezó todo?
Todo comenzó con un intento de resolver el problema de la cancelación garantizada de temporizadores. La esencia del problema es que cuando se envía un mensaje retrasado o periódico, el programador puede cancelar la entrega del mensaje. Por ejemplo:
auto timer_id = so_5::send_periodic<my_message>(my_agent, 10s, 10s, ...); ...
Después de llamar a
timer_id.release (), el temporizador ya no enviará nuevas instancias del mensaje my_message. Pero esas copias que ya se han enviado y están en la cola de los destinatarios no irán a ninguna parte. Con el tiempo, se extraerán de estas mismas colas y se transferirán a los agentes receptores para su procesamiento.
Este problema es una consecuencia de los principios básicos de funcionamiento de SObjectizer-5 y no tiene una solución simple debido a que SObjectizer no puede extraer mensajes de las colas. No puede porque las colas en SObjectizer pertenecen a despachadores, los despachadores son diferentes, sus colas también están organizadas de manera diferente. Incluyendo que
hay despachadores que no son parte del SObjectizer, y SObjectizer, en principio, no puede saber cómo funcionan estos despachadores.
En general, existe tal característica en los temporizadores nativos de SObjectizer. No es que estropee demasiado a los desarrolladores. Pero se debe tener especial cuidado. Especialmente para principiantes que recién se están familiarizando con el marco.
Y luego, finalmente, las manos fueron al punto de proponer una solución a este problema.
¿Qué camino de solución fue elegido?
En un
artículo anterior , se consideraron dos posibles opciones. La primera opción no requería modificaciones al mecanismo de entrega de mensajes en SObjectizer, pero requería que el programador cambiara explícitamente el tipo de mensaje enviado / recibido.
La segunda opción requería la modificación del mecanismo de entrega de mensajes SObjectizer. Se eligió este camino, ya que permitió ocultar al destinatario del mensaje el hecho de que el mensaje se envió de alguna manera específica.
¿Qué ha cambiado en SObjectizer?
Nuevo concepto: sobre con mensaje dentro
El primer componente de la solución implementada es la adición de un concepto como un sobre a SObjectizer. Un sobre es un mensaje especial, dentro del cual se encuentra el mensaje actual (carga útil). SObjectizer entrega el sobre con el mensaje al destinatario casi de la forma habitual. La diferencia fundamental en el procesamiento de sobres se detecta solo en la última etapa de entrega:
- al entregar un mensaje normal, el agente receptor simplemente busca un controlador para este tipo de mensaje y, si se encuentra dicho controlador, se llama al controlador encontrado y el mensaje entregado se devuelve como un parámetro;
- y al entregar el sobre con el mensaje después de encontrar el controlador, primero se hace un intento para sacar el mensaje del sobre. Y solo si el sobre dio el mensaje almacenado en él, solo entonces se llama al controlador.
Aquí hay dos puntos clave que tienen un gran impacto en por qué y cómo se pueden usar los sobres de mensajes.
El primer punto clave es que se solicita un mensaje de un sobre solo cuando se encuentra un controlador de mensaje en el destinatario. Es decir solo cuando el mensaje realmente se haya entregado al destinatario y el destinatario esté aquí y ahora procesará este mensaje.
El segundo punto clave aquí es que el sobre puede no revelar el mensaje que contiene. Es decir, por ejemplo, un sobre puede verificar la hora actual y decidir que se han perdido todas las fechas de entrega y, por lo tanto, el mensaje ha dejado de ser relevante y no se puede procesar. Por lo tanto, el sobre no dará el mensaje. En consecuencia, SObjectizer simplemente ignorará este sobre y no tomará ninguna acción adicional.
¿Cómo es un sobre?
Un sobre es una implementación de la interfaz envolvente_t, que se define de la siguiente manera:
class SO_5_TYPE envelope_t : public message_t { public: ...
Es decir Un sobre es esencialmente el mismo mensaje que todos los demás. Pero con un atributo especial, que es devuelto por el método so5_message_kind ().
El programador puede desarrollar sus sobres heredando de envolvente_t (o, más convenientemente, de
so_5 :: extra :: envolped_msg :: just_envelope_t ) y anulando los métodos de enlace handler_found_hook () y transformación_hook ().
Dentro de los métodos de enlace, el desarrollador del sobre decide si quiere dar el mensaje dentro del sobre para su procesamiento / transformación o no. Si lo desea, entonces el desarrollador debe llamar al método invoke () y al objeto invocador. Si no quiere, no llama, en este caso se ignorará el sobre y su contenido.
¿Cómo resuelven los sobres el problema de cancelar los temporizadores?
La solución, que ahora se implementa en so_5_extra en forma del espacio de nombres so_5 :: extra :: revocable_timer, es muy simple: el envío especial de un mensaje pendiente o periódico crea un sobre especial, dentro del cual se ubica no solo el mensaje en sí, sino también el indicador atómico revocado. Si se borra esta bandera, el mensaje se considera relevante. Si se establece, el mensaje se considera retirado.
Cuando se llama al método de enlace en el sobre, el sobre verifica el valor de la bandera revocada. Si se establece la bandera, el sobre no muestra un mensaje. Por lo tanto, el mensaje no se procesa incluso si el temporizador ya ha logrado poner el mensaje en la cola del receptor.
Extensión de interfaz abstract_message_box_t
Agregar la interfaz envolvente_t es solo una parte de la implementación de sobres en SObjectizer. La segunda parte es tener en cuenta el hecho de la existencia de sobres en el mecanismo de entrega de mensajes dentro del SObjectizer.
Aquí, desafortunadamente, no podría prescindir sin hacer que los cambios sean visibles para el usuario. En particular, en la clase abstract_message_box_t, que define la interfaz de todos los buzones en SObjectizer, fue necesario agregar otro método virtual:
virtual void do_deliver_enveloped_msg( const std::type_index & msg_type, const message_ref_t & message, unsigned int overlimit_reaction_deep );
Este método es responsable de entregar un sobre de mensaje con un mensaje de tipo msg_type al receptor. Tal entrega puede diferir en los detalles de implementación dependiendo de qué tipo de mbox sea.
Al agregar do_deliver_enveloped_msg () a abstract_message_box_t, teníamos una opción: convertirlo en un método virtual puro u ofrecer algún tipo de implementación predeterminada.
Si hiciéramos do_deliver_enveloped_msg () un método virtual puro, entonces romperíamos la compatibilidad entre las versiones de SObjectizer en la rama 5.5. Después de todo, los usuarios que escribieron sus propias implementaciones de mbox tendrían que modificar sus propios mboxes al cambiar a SObjectizer-5.5.23, de lo contrario no podrían compilar con la nueva versión de SObjectizer.
No queríamos esto, por lo que no hicimos do_deliver_enveloped_msg () un método virtual puro en v.5.5.23. Tiene una implementación predeterminada que solo arroja una excepción. Por lo tanto, los usuarios personalizados mbox-s podrán continuar trabajando normalmente con mensajes regulares, pero se negarán automáticamente a aceptar sobres. Encontramos este comportamiento más aceptable. Además, en la etapa inicial, es poco probable que los sobres con mensajes sean ampliamente utilizados, y es poco probable que en la "naturaleza" se encuentren a menudo implementaciones personalizadas de SObjectizer mboxes;)
Además, hay muchas posibilidades de que en las versiones principales posteriores de SObjectizer, donde no veremos la compatibilidad con la rama 5.5, la interfaz abstract_message_box_t sufra cambios importantes. Pero ya nos estamos adelantando ...
Cómo enviar sobres con mensajes
SObjectizer-5.5.23 en sí mismo no proporciona un medio simple para enviar sobres. Se supone que se está desarrollando un tipo específico de sobre y herramientas apropiadas para una tarea específica para enviar convenientemente sobres de un tipo específico. Un ejemplo de esto se puede ver en
so_5 :: extra :: revocable_timer , donde necesita no solo enviar el sobre, sino también darle al usuario un timer_id especial.
Para situaciones más simples, puede usar las herramientas de
so_5 :: extra :: wrapped_msg . Por ejemplo, así es como se envía un mensaje con una restricción dada en el momento de su entrega:
Para que todo sea divertido: sobres en sobres
Los sobres están diseñados para llevar algunos mensajes dentro de sí mismos. Pero cuales?
Cualquiera.
Y esto nos lleva a una pregunta interesante: ¿es posible poner un sobre dentro de otro sobre?
Si puedes. Tanto como quieras La profundidad de anidamiento está limitada solo por el sentido común del desarrollador y la profundidad de la pila para la llamada recursiva handler_found_hook / conversion_hook.
Al mismo tiempo, SObjectizer se dirige a los desarrolladores de sus propios sobres: el sobre no debe pensar en lo que contiene: un mensaje específico u otro sobre. Cuando se llama al método de enlace en el sobre y el sobre decide que puede dar su contenido, el sobre simplemente llama a invoke () en handler_invoker_t y pasa un enlace a su contenido en invoke (). Y ya invocar () dentro descubrirá a qué se enfrenta. Y si este es otro sobre, invoke () llamará al método de enlace requerido en este sobre.
Usando el kit de herramientas que se muestra arriba de so_5 :: extra :: wrapped_msg, el usuario puede hacer varios sobres anidados como este:
so_5::extra::enveloped_msg::make<my_message>(...)
Algunos ejemplos de uso de sobres
Ahora, después de haber recorrido los aspectos internos de SObjectizer-5.5.23, es hora de pasar a la parte de aplicación más útil para los usuarios. A continuación hay algunos ejemplos que se basan en lo que ya está implementado en so_5_extra, o usan las herramientas de so_5_extra.
Temporizadores revocables
Dado que toda esta cocina con sobres fue concebida para resolver el problema de la recuperación garantizada de los mensajes del temporizador, veamos qué sucedió al final. Usaremos el ejemplo de so_5_extra-1.2.0, que usa las herramientas del nuevo espacio de nombres so_5 :: extra :: revocable_timer:
Código de ejemplo con temporizadores revocables #include <so_5_extra/revocable_timer/pub.hpp> #include <so_5/all.hpp> namespace timer_ns = so_5::extra::revocable_timer; class example_t final : public so_5::agent_t { // , // . struct first_delayed final : public so_5::signal_t {}; struct second_delayed final : public so_5::signal_t {}; struct last_delayed final : public so_5::signal_t {}; struct periodic final : public so_5::signal_t {}; // . timer_ns::timer_id_t m_first; timer_ns::timer_id_t m_second; timer_ns::timer_id_t m_last; timer_ns::timer_id_t m_periodic; public : example_t( context_t ctx ) : so_5::agent_t{ std::move(ctx) } { so_subscribe_self() .event( &example_t::on_first_delayed ) .event( &example_t::on_second_delayed ) .event( &example_t::on_last_delayed ) .event( &example_t::on_periodic ); } void so_evt_start() override { using namespace std::chrono_literals; // ... m_first = timer_ns::send_delayed< first_delayed >( *this, 100ms ); m_second = timer_ns::send_delayed< second_delayed >( *this, 200ms ); m_last = timer_ns::send_delayed< last_delayed >( *this, 300ms ); // ... . m_periodic = timer_ns::send_periodic< periodic >( *this, 75ms, 75ms ); // 220ms. // first_delaye, second_delayed // periodic. std::cout << "hang the agent..." << std::flush; std::this_thread::sleep_for( 220ms ); std::cout << "done" << std::endl; } private : void on_first_delayed( mhood_t<first_delayed> ) { std::cout << "first_delayed received" << std::endl; // second_delayed periodic. // , // . m_second.revoke(); m_periodic.revoke(); } void on_second_delayed( mhood_t<second_delayed> ) { std::cout << "second_delayed received" << std::endl; } void on_last_delayed( mhood_t<last_delayed> ) { std::cout << "last_delayed received" << std::endl; so_deregister_agent_coop_normally(); } void on_periodic( mhood_t<periodic> ) { std::cout << "periodic received" << std::endl; } }; int main() { so_5::launch( [](so_5::environment_t & env) { env.register_agent_as_coop( "example", env.make_agent<example_t>() ); } ); return 0; }
Que tenemos aqui
Tenemos un agente que primero inicia varios mensajes del temporizador y luego bloquea su hilo de trabajo por un tiempo. Durante este tiempo, el temporizador logra poner en cola al agente varias solicitudes como resultado de los temporizadores activados: varias instancias periódicas, una primera demorada y una segunda demorada cada una.
En consecuencia, cuando un agente desbloquea su hilo, debe recibir el primer periódico y el primer retraso. Al procesar first_delayed, el agente cancela la entrega de periódico y second_delayed. Por lo tanto, estas señales no deben llegar al agente, independientemente de si ya están en la cola del agente o no (y lo están).
Nos fijamos en el resultado del ejemplo:
hang the agent...done periodic received first_delayed received last_delayed received
Si lo es. Obtuve el primer periódico y el primer retraso. Entonces no hay periódicos ni segundos retrasados.
Pero si en el ejemplo reemplazamos los "temporizadores" de so_5 :: extra :: revocable_timer con los temporizadores estándar de SObjectizer, el resultado será diferente: todas las instancias de señales periódicas y de segundo retardo que ya ingresaron en la cola del agente llegarán al agente.
Mensajes restringidos de tiempo de entrega
Otra cosa útil, a veces, que estará disponible en so_5_extra-1.2.0 es la entrega de mensajes con un límite de tiempo. Por ejemplo, el agente request_handler envía un mensaje verificar_firma al agente crypto_master. Al mismo tiempo, request_handler quiere que verificar_signature se entregue en 5 segundos. Si esto no sucede, entonces no tendrá sentido el procesamiento de verity_signature, el agente request_handler ya detendrá su trabajo.
Y el agente de crypto_master es un compañero al que le gusta convertirse en un "cuello de botella": a veces comienza a disminuir la velocidad. En ese momento, los mensajes se acumulan en la cola, como la firma de verificación anterior, que puede esperar hasta que se alivie crypto_master.
Supongamos que request_handler envió un mensaje de verificación de firma al agente crypto_master, pero luego crypto_master se empantanó y se atascó durante 10 segundos. El agente request_handler ya se ha "caído", es decir Ya envió a todos una denegación de servicio y completó su trabajo. ¡Pero el mensaje generate_signature permanece en la cola crypto_master! Entonces, cuando crypto_master "se despega", tomará este mensaje y procesará este mensaje. Aunque esto ya no es necesario.
Usando el nuevo sobre so_5 :: extra :: envolped_msg :: time_limited_delivery_t, podemos resolver este problema: el agente request_handler enviará verifique_signature time_limited_delivery_t incluido en el sobre con un límite de tiempo de entrega:
so_5::extra::enveloped_msg::make<verify_signature>(...) .envelope<so_5::extra::enveloped_msg::time_limited_delivery_t>(5s) .send_to(crypto_master_mbox);
Ahora, si crypto_master "se pega" y no logra verificar_firma en 5 segundos, el sobre simplemente no enviará este mensaje para su procesamiento. Y crypto_master no hará el trabajo que nadie más necesita.
Informes de entrega de destinatarios
Y finalmente, un ejemplo de algo curioso que no se implementa regularmente en SObjectizer o so_5_extra, pero que se puede hacer de forma independiente.
A veces desea recibir del SObjectizer algo como un mensaje de "informe de entrega" al destinatario. Después de todo, es una cosa cuando el mensaje llegó al destinatario, pero el destinatario por alguna razón no respondió. Otra cosa es cuando el mensaje no llegó al destinatario en absoluto. Por ejemplo, fue bloqueado por
un mecanismo de protección de sobrecarga del agente . En el primer caso, se puede omitir un mensaje al que no esperamos una respuesta. Pero en el segundo caso, puede tener sentido volver a enviar el mensaje después de un tiempo.
Ahora consideraremos cómo se puede implementar el mecanismo más simple de "informes de entrega" utilizando sobres.
Entonces, primero hacemos los pasos preparatorios necesarios:
#include <so_5_extra/enveloped_msg/just_envelope.hpp> #include <so_5_extra/enveloped_msg/send_functions.hpp> #include <so_5/all.hpp> using namespace std::chrono_literals; namespace envelope_ns = so_5::extra::enveloped_msg; using request_id_t = int;
Ahora podemos definir los mensajes que se utilizarán en el ejemplo. El primer mensaje es una solicitud para realizar algunas acciones que necesitamos. Y el segundo mensaje es una confirmación de que el primer mensaje llegó al destinatario:
struct request_t final { request_id_t m_id; std::string m_data; }; struct delivery_receipt_t final {
A continuación, podemos definir un agente procesador_t que procesará mensajes de tipo request_t. Pero el procesamiento será con una imitación de "pegado". Es decir procesa request_t, después de lo cual cambia su estado de st_normal a st_busy. En el estado st_busy, no hace nada e ignora todos los mensajes que le llegan.
Esto significa que si el agente procesador_t envía tres mensajes consecutivos request_t, procesará el primero y los otros dos serán arrojados, porque Al procesar el primer mensaje, el agente irá a st_busy e ignorará lo que le llegará mientras está en st_busy.
En st_busy, el agente procesador_t pasará 2 segundos, después de lo cual volverá nuevamente a st_normal y estará listo para procesar nuevos mensajes.
Así se ve el agente procesador_t:
class processor_t final : public so_5::agent_t {
Ahora podemos definir el agente request_generator_t, que tiene un montón de solicitudes que deben entregarse al procesador_t. El agente request_generator_t envía el paquete completo cada 3 segundos, y luego espera la confirmación de entrega en forma de delivery_receipt_t.
Cuando llega delivery_recept_t, el agente request_generator_t arroja la solicitud entregada fuera del paquete. Si el paquete está completamente vacío, se completa el ejemplo. Si queda algo más, el paquete restante se enviará nuevamente cuando llegue la próxima vez para reenviar.
Así que aquí está el código de agente request_generator_t. Es bastante voluminoso, pero primitivo.
Solo puede prestar atención a los aspectos internos del método send_requests (), en el que se envían mensajes request_t, encerrados en un sobre especial.Requests_generator_t código de agente class requests_generator_t final : public so_5::agent_t {
Ahora tenemos mensajes y agentes que deben usar estos mensajes para comunicarse. Solo quedaba una pequeña cosa: hacer que los mensajes delivery_receipt_t lleguen de alguna manera al entregar request_t al procesador_t.Esto se hace usando este sobre: class custom_envelope_t final : public envelope_ns::just_envelope_t { // . const so_5::mbox_t m_to; // ID . const request_id_t m_id; public: custom_envelope_t(so_5::message_ref_t payload, so_5::mbox_t to, request_id_t id) : envelope_ns::just_envelope_t{std::move(payload)} , m_to{std::move(to)} , m_id{id} {} void handler_found_hook(handler_invoker_t & invoker) noexcept override { // , . // . so_5::send<delivery_receipt_t>(m_to, m_id); // . envelope_ns::just_envelope_t::handler_found_hook(invoker); } };
En general, no hay nada complicado. Heredamos de so_5 :: extra :: envped_msg :: just_envelope_t. Este es un tipo de sobre auxiliar que almacena el mensaje encerrado en él y proporciona la implementación básica de los ganchoshandler_found_hook () y transformación_hook (). Por lo tanto, solo podemos guardar los atributos que necesitamos dentro de custom_envelope_t y enviar delivery_receipt_t dentro del gancho handler_found_hook ().Eso, de hecho, es todo.
Si ejecutamos este ejemplo, obtenemos lo siguiente: sending request: (0, First) sending request: (1, Second) sending request: (2, Third) sending request: (3, Four) processor: on_request(0, First) request delivered: 0 time to resend requests, pending requests: 3 sending request: (1, Second) sending request: (2, Third) sending request: (3, Four) processor: on_request(1, Second) request delivered: 1 time to resend requests, pending requests: 2 sending request: (2, Third) sending request: (3, Four) processor: on_request(2, Third) request delivered: 2 time to resend requests, pending requests: 1 sending request: (3, Four) processor: on_request(3, Four) request delivered: 3
Además, debo decir que en la práctica, un simple custom_envelope_t para generar informes de entrega no es adecuado. Pero si alguien está interesado en este tema, entonces puede discutirse en los comentarios y no aumentar el volumen del artículo.¿Qué más se podría hacer con los sobres?
Gran pregunta!
A lo que nosotros mismos no tenemos una respuesta integral. Probablemente, las posibilidades están limitadas solo por la imaginación de los usuarios. Bueno, si para la realización de fantasías en SObjectizer falta algo, entonces esto se nos puede decir. Siempre escuchamos Y, lo que es más importante, a veces incluso lo hacemos :)Integración de agentes con mchain
Hablando un poco más en serio, esa es otra característica que me gustaría tener de vez en cuando y que incluso se planeó para so_5_extra-1.2.0. Pero, lo más probable, no se incluirá en la versión 1.2.0.Se trata de simplificar la integración de mchains y agentes.El hecho es que inicialmente se agregaron mchains a SObjectizer para simplificar la comunicación de los agentes con otras partes de la aplicación que están escritas sin agentes. Por ejemplo, existe el hilo principal de la aplicación, en el cual el usuario interactúa usando la GUI. Y hay varios agentes-trabajadores que hacen el trabajo "duro" de fondo. Enviar un mensaje a un agente desde el hilo principal no es un problema: solo llame al envío regular. Pero, ¿cómo transferir la información de regreso?Para esto, se agregaron mchain-s.Pero con el tiempo, resultó que las cadenas pueden jugar un papel mucho más importante. Es posible, en principio, hacer aplicaciones multiproceso en SObjectizer sin ningún agente, solo en mchain-ahs (más detalles aquí ). Y puede usar mchain-s como un medio para equilibrar la carga de los agentes. Como mecanismo para resolver problemas productor-consumidor.El problema con el productor-consumidor es que si el productor genera mensajes más rápido de lo que el consumidor puede manejarlos, entonces estamos en problemas. Las colas de mensajes crecerán, el rendimiento puede degradarse con el tiempo o la aplicación se bloqueará por completo debido al agotamiento de la memoria.La solución habitual que propusimos usar en este caso es usarun par de agentes coleccionistas e intérpretes . También puede usar límites de mensajes (ya sea como el mecanismo de protección principal o como una adición al recopilador-intérprete). Pero escribir coleccionista-intérprete requiere trabajo adicional del programador.Pero mchains podría usarse para estos fines con el mínimo esfuerzo del desarrollador. Entonces, el productor pondría el siguiente mensaje en mchain, y el consumidor tomaría mensajes de esta mchain.Pero el problema es que cuando el consumidor es un agente, no es muy conveniente que un agente trabaje con mchain a través de las funciones de recepción () y selección () disponibles. Y se podría tratar de eliminar este inconveniente con la ayuda de alguna herramienta para integrar agentes y mchain-s.Al desarrollar dicha herramienta, será necesario resolver varios problemas. Por ejemplo, cuando llega un mensaje en mchain, ¿en qué punto debe extraerse de mchain? Si el consumidor es gratuito y no procesa nada, puede recoger el mensaje de mchain de inmediato y entregárselo al agente del consumidor. Si ya se ha enviado un mensaje al consumidor desde mchain, aún no ha logrado procesar este mensaje, pero un nuevo mensaje ya ha llegado a mchain ... ¿Qué se debe hacer en este caso?Se especula que los sobres pueden ayudar en este caso. Entonces, cuando tomamos el primer mensaje de mchain y lo enviamos al consumidor, envolvemos este mensaje en un sobre especial. Cuando el sobre ve que el mensaje ha sido entregado y procesado, solicita el siguiente mensaje de mchain (si hay uno).Por supuesto, no todo es tan simple aquí. Pero hasta ahora parece bastante solucionable. Y, espero, un mecanismo similar aparecerá en una de las próximas versiones de so_5_extra.¿Vamos a abrir la caja de Pandora?
Cabe señalar que con nosotros las capacidades adicionales en sí mismas causan sentimientos duales.Por un lado, los sobres ya han permitido / permiten hacer cosas que se mencionaron anteriormente (pero que solo soñaron con algo). Por ejemplo, esta es una cancelación garantizada de temporizadores y una restricción en el tiempo de entrega, informes de entrega, la capacidad de recuperar un mensaje enviado previamente.Por otro lado, no está claro a qué conducirá esto posteriormente. Después de todo, puede crear un problema con cualquier oportunidad si comienza a usar esta oportunidad donde necesita y donde no. ¿Entonces tal vez abrimos la caja de Pandora y todavía no imaginamos lo que nos espera?Solo queda ser paciente y ver a dónde nos llevará todo esto.Acerca de los planes de desarrollo inmediato de SObjectizer en lugar de concluir
En lugar de una conclusión, quiero hablar sobre cómo vemos el futuro muy cercano (y no solo) de SObjectizer. Si alguien no está contento con algo en nuestros planes, puede hablar e influir en cómo se desarrollará SObjectizer-5.Las primeras versiones beta de SObjectizer-5.5.23 y so_5_extra-1.2.0 ya están reparadas y disponibles para descargar y experimentar. Todavía habrá mucho trabajo por hacer en el área de documentación y casos de uso. Por lo tanto, el lanzamiento oficial está previsto para la primera década de noviembre. Si funciona antes, lo haremos antes.El lanzamiento de SObjectizer-5.5.23 parece significar que la evolución de la rama 5.5 está llegando a su fin. El primer lanzamiento de este hilo tuvo lugar hace cuatro años, en octubre de 2014.. Desde entonces, SObjectizer-5 ha evolucionado dentro de la rama 5.5 sin ningún cambio importante entre las versiones. No fue facil. Especialmente considerando el hecho de que todo este tiempo tuvimos que mirar hacia atrás a los compiladores que tenían un soporte lejos de ser ideal para C ++ 11.Ahora no vemos ninguna razón para mirar hacia atrás en la compatibilidad dentro de la rama 5.5, y especialmente en los compiladores C ++ más antiguos. Lo que podría estar justificado en 2014, cuando C ++ 14 se estaba preparando para ser adoptado oficialmente, y C ++ 17 aún no estaba en el horizonte, ahora se ve completamente diferente.Además, en SObjectizer-5.5 ya se ha acumulado una buena cantidad de rake y copias de seguridad, que aparecieron debido a esta misma compatibilidad y que complican el desarrollo posterior de SObjectizer.Por lo tanto, en los próximos meses vamos a actuar de acuerdo con el siguiente escenario:1. Desarrollo de la próxima versión de so_5_extra, en la que quiero agregar herramientas para simplificar la redacción de pruebas para agentes. Aún no está claro si será so_5_extra-1.3.0 (es decir, con cambios de ruptura en relación con 1.2.0) o si será so_5_extra-1.2.1 (es decir, sin cambios de ruptura). Veamos cómo va. Solo está claro que la próxima versión de so_5_extra se basará en SObjectizer-5.5.1a. Si para la próxima versión de so_5_extra necesita hacer algo adicional en SObjectizer-5.5, se lanzará la próxima versión 5.5.24. Si para so_5_extra no será necesario realizar mejoras en el núcleo de SObjectizer, la versión 5.5.23 será la última versión significativa en el marco de la rama 5.5. Saldrán lanzamientos menores de corrección de errores. Pero el desarrollo de la rama 5.5 se detiene en la versión 5.5.23 o 5.5.24.2. Luego se lanzará una versión de SObjectizer-5.6.0, que abrirá una nueva rama. En la rama 5.6, limpiaremos el código SObjectizer de todas las muletas y copias de seguridad acumuladas, así como de la basura vieja que durante mucho tiempo se ha marcado como obsoleta. Es probable que algunas cosas se sometan a una refactorización (por ejemplo, abstract_message_box_t se puede cambiar), pero difícilmente cardinal. Los principios básicos de trabajo y las características de SObjectizer-5.5 en SObjectizer-5.6 permanecerán en la misma forma.SObjectizer-5.6 ya requerirá C ++ 14 (al menos en el nivel GCC-5.5). Los compiladores de Visual C ++ debajo de VC ++ 15 (que es de Visual Studio 2017) no serán compatibles.Consideramos la rama 5.6 como una rama estable de SObjectizer, que será relevante hasta que aparezca la primera versión de SObjectizer-5.7.Me gustaría lanzar la versión 5.6.0 a principios de 2019, tentativamente en febrero.3. Después de estabilizar la rama 5.6, nos gustaría comenzar a trabajar en la rama 5.7, en la que podríamos revisar algunos principios básicos del trabajo de SObjectizer. Por ejemplo, abandone por completo los despachadores públicos, dejando solo los privados. Rehacer el mecanismo de las cooperativas y sus relaciones entre padres e hijos, eliminando así el cuello de botella durante el registro / desregistro de las cooperativas. Eliminar la división por mensaje / señal. Permita que solo send / send_delayed / send_periodic envíe mensajes, y oculte los métodos de delivery_message y schedule_timer "debajo del capó". Modifique el mecanismo para enviar mensajes de modo que elimine completamente dynamic_casts de este proceso o reduzca al mínimo.En general, hay dónde dar la vuelta. Al mismo tiempo, SObjectizer-5.7 ya requerirá C ++ 17, independientemente de C ++ 14.Si miras las cosas sin gafas de color rosa, es bueno que la versión 5.7.0 tenga lugar a fines del otoño de 2019. La principal versión de trabajo de SObjectizer para 2019 será la rama 5.6.4. Paralelo a todo esto, se desarrollará so_5_extra. Probablemente, la versión so_5_extra-2 se lanzará junto con SObjectizer-5.6, que incorporará una nueva funcionalidad en el transcurso de 2019, pero basada en SObjectizer-5.6.Por lo tanto, nosotros mismos vemos una evolución progresiva de SObjectizer-5 con una revisión gradual de algunos de los principios básicos de SObjectizer-5. Al mismo tiempo, intentaremos hacer esto de la forma más fluida posible para que sea posible cambiar de una versión a otra con un mínimo de dolor.Sin embargo, si alguien quisiera cambios más dramáticos y significativos de SObjectizer, entonces tenemos algunas ideas al respecto . En pocas palabras: puede rehacer SObjectizer como desee, hasta implementar SObjectizer-6 para otro lenguaje de programación. Pero no lo haremos por nuestra cuenta, ya que esto sucede con la evolución de SObjectizer-5.Eso es probablemente todo.
Los comentarios al artículo anterior resultaron ser una discusión buena y constructiva. Sería útil para nosotros si ocurriera una discusión similar esta vez. Como siempre, estamos listos para responder cualquier pregunta, pero a las sensatas, y con gusto.Y para los lectores más pacientes que han llegado a estas líneas, muchas gracias por el tiempo dedicado a leer el artículo.