En programación informática , un thunk es una subrutina que se utiliza para insertar un cálculo en otra subrutina. Los thunks se utilizan principalmente para retrasar un cálculo hasta que se necesite su resultado, o para insertar operaciones al principio o al final de la otra subrutina. Tienen muchas otras aplicaciones en la generación de código de compiladores y la programación modular .
El término se originó como una forma irregular y caprichosa del verbo pensar . Se refiere al uso original de thunks en los compiladores ALGOL 60 , que requerían un análisis especial (pensamiento) para determinar qué tipo de rutina generar. [ 1 ] [ 2 ]
Fondo
En los primeros años de la investigación sobre compiladores , se experimentó ampliamente con diferentes estrategias de evaluación . Una cuestión clave era cómo compilar una llamada a subrutina si los argumentos podían ser expresiones matemáticas arbitrarias en lugar de constantes. Un enfoque, conocido como " llamada por valor ", calcula todos los argumentos antes de la llamada y luego pasa los valores resultantes a la subrutina. En el enfoque rival de " llamada por nombre ", la subrutina recibe la expresión del argumento sin evaluar y debe evaluarla.
Una implementación simple de "llamada por nombre" podría sustituir el código de una expresión de argumento por cada aparición del parámetro correspondiente en la subrutina, pero esto puede producir múltiples versiones de la subrutina y múltiples copias del código de la expresión. Como mejora, el compilador puede generar una subrutina auxiliar, llamada thunk , que calcula el valor del argumento. La dirección y el entorno [ a ] de esta subrutina auxiliar se pasan a la subrutina original en lugar del argumento original, donde se puede llamar tantas veces como sea necesario. Peter Ingerman describió por primera vez los thunks en referencia al lenguaje de programación ALGOL 60, que admite la evaluación por llamada por nombre. [ 4 ]
Aplicaciones
Programación funcional
Aunque la industria del software estandarizó en gran medida la evaluación por valor y por referencia , [ 5 ] el estudio activo de la evaluación por nombre continuó en la comunidad de programación funcional . Esta investigación produjo una serie de lenguajes de programación de evaluación perezosa en los que alguna variante de evaluación por nombre es la estrategia de evaluación estándar. Los compiladores para estos lenguajes, como el Glasgow Haskell Compiler , se han basado en gran medida en thunks, con la característica adicional de que los thunks guardan su resultado inicial para que puedan evitar recalcularlo; [ 6 ] esto se conoce como memorización o evaluación por necesidad .
Los lenguajes de programación funcional también han permitido a los programadores generar explícitamente funciones auxiliares (thunks). Esto se logra en el código fuente al encapsular una expresión de argumento en una función anónima sin parámetros propios. Esto impide que la expresión se evalúe hasta que una función receptora llame a la función anónima, consiguiendo así el mismo efecto que la llamada por nombre. [ 7 ] La adopción de funciones anónimas en otros lenguajes de programación ha hecho que esta capacidad esté ampliamente disponible.
Programación orientada a objetos
Los thunks son útiles en plataformas de programación orientada a objetos que permiten que una clase herede múltiples interfaces , lo que da lugar a situaciones en las que se puede llamar al mismo método a través de varias interfaces. El siguiente código ilustra una situación de este tipo en C++ .
clase A { público : virtual int Access () const { return value_ ; }privado : int valor_ ; };clase B { público : virtual int Access () const { return value_ ; }privado : int valor_ ; };clase C : público A , público B { público : int Access () const override { return better_value_ ; }privado : int mejor_valor_ ; };int use ( B * b ) { return b -> Access (); }int main () { // ... B some_b ; use ( & some_b ); C some_c ; use ( & some_c ); }En este ejemplo, el código generado para cada una de las clases A, B y C incluirá una tabla de despacho que se puede usar para llamar Accessa un objeto de ese tipo, a través de una referencia del mismo tipo. La clase C tendrá una tabla de despacho adicional, que se usa para llamar Accessa un objeto de tipo C a través de una referencia de tipo B. La expresión b->Access()usará la tabla de despacho propia de B o la tabla adicional de C, dependiendo del tipo de objeto al que se refiere b. Si se refiere a un objeto de tipo C, el compilador debe asegurarse de que Accessla implementación de C reciba una dirección de instancia para todo el objeto C, en lugar de la parte B heredada de ese objeto. [ 8 ]
Como solución directa a este problema de ajuste de punteros, el compilador puede incluir un desplazamiento entero en cada entrada de la tabla de despacho. Este desplazamiento es la diferencia entre la dirección de la referencia y la dirección requerida por la implementación del método. El código generado para cada llamada a través de estas tablas de despacho debe recuperar el desplazamiento y usarlo para ajustar la dirección de la instancia antes de llamar al método.
La solución descrita anteriormente presenta problemas similares a la implementación ingenua de llamada por nombre: el compilador genera varias copias de código para calcular un argumento (la dirección de instancia), a la vez que aumenta el tamaño de la tabla de despacho para almacenar los desplazamientos. Como alternativa, el compilador puede generar una función auxiliar de ajuste junto con la implementación de C Accessque ajuste la dirección de instancia en la cantidad requerida y luego llame al método. Esta función auxiliar puede aparecer en la tabla de despacho de C para B, eliminando así la necesidad de que quienes la llaman ajusten la dirección por sí mismos. [ 9 ]
Interoperabilidad
Las funciones auxiliares (thunks) se han utilizado ampliamente para proporcionar interoperabilidad entre módulos de software cuyas rutinas no pueden llamarse directamente entre sí. Esto puede ocurrir porque las rutinas tienen convenciones de llamada diferentes , se ejecutan en distintos modos de CPU o espacios de direcciones , o al menos una se ejecuta en una máquina virtual . Un compilador (u otra herramienta) puede resolver este problema generando una función auxiliar que automatiza los pasos adicionales necesarios para llamar a la rutina de destino, ya sea transformando argumentos, copiándolos a otra ubicación o cambiando el modo de CPU. Una función auxiliar exitosa minimiza el trabajo adicional que debe realizar quien realiza la llamada en comparación con una llamada normal.
Gran parte de la literatura sobre thunks de interoperabilidad se relaciona con varias plataformas Wintel , incluyendo MS-DOS , OS/2 , [ 10 ] Windows [ 11 ] [ 12 ] [ 13 ] [ 14 ] y .NET , y con la transición del direccionamiento de memoria de 16 bits a 32 bits . A medida que los clientes han migrado de una plataforma a otra, los thunks han sido esenciales para admitir el software heredado escrito para las plataformas más antiguas. UEFI CSM es otro ejemplo de cómo hacer thunk para cargadores de arranque heredados .
La transición de código de 32 bits a 64 bits en x86 también utiliza una forma de thunking ( WoW64 ). Sin embargo, debido a que el espacio de direcciones de x86-64 es mayor que el disponible para el código de 32 bits, el antiguo mecanismo de "thunk genérico" no podía utilizarse para llamar a código de 64 bits desde código de 32 bits. [ 15 ] El único caso en el que código de 32 bits llama a código de 64 bits se da en el thunking de WoW64 de las API de Windows a 32 bits.
Superposiciones y enlaces dinámicos
En sistemas que carecen de hardware de memoria virtual automática , las funciones thunk pueden implementar una forma limitada de memoria virtual conocida como superposiciones . Con las superposiciones, un desarrollador divide el código de un programa en segmentos que se pueden cargar y descargar de forma independiente, e identifica los puntos de entrada a cada segmento. Un segmento que llama a otro segmento debe hacerlo indirectamente a través de una tabla de ramificación . Cuando un segmento está en memoria, las entradas de su tabla de ramificación saltan al segmento. Cuando un segmento se descarga, sus entradas se reemplazan con "funciones thunk de recarga" que pueden recargarlo bajo demanda. [ 16 ]
De manera similar, los sistemas que enlazan dinámicamente módulos de un programa en tiempo de ejecución pueden usar thunks para conectar los módulos. Cada módulo puede llamar a los demás a través de una tabla de thunks que el enlazador completa al cargar el módulo. De esta forma, los módulos pueden interactuar sin necesidad de conocer previamente su ubicación en la memoria. [ 17 ]
Véase también
Tecnologías Thunk
- Interfaz de modo protegido de DOS (DPMI)
- Servicios de modo protegido de DOS (DPMS)
- J/Direct
- Capa de Microsoft para Unicode
- Servicios de invocación de plataforma
- Win32
- Windows en Windows
- Módulo de soporte de compatibilidad
- WoW64
- libffi
- Vino (desde la versión 9.0) [ 18 ]
Conceptos relacionados
Notas
Referencias
- ↑ Eric Raymond rechaza «un par de mitos onomatopéyicos que circulan sobre el origen de este término» y cita a los inventores del thunk, quienes recuerdan que el término «fue acuñado después de que se dieran cuenta (en la madrugada, tras horas de discusión) de que el tipo de argumento en Algol-60 podía deducirse de antemano con un poco de reflexión durante la compilación [...] En otras palabras, ya se había pensado en ello; por lo tanto, se le denominó thunk , que es «el pretérito de "pensar" a las dos de la mañana». Véase: Raymond, Eric S. (1996). Raymond, Eric S. (ed.). The New Hacker's Dictionary . MIT Press. p. 445. ISBN 9780262680929. Consultado el 25 de mayo de 2015 .
- ↑ Véase Ingerman (1961): "El traductor sabe qué tipo de thunk crear considerando la formación del parámetro real y las declaraciones previamente analizadas... [C]uando se compila una declaración de procedimiento, el traductor, nuevamente observando la sintaxis, sabe qué tipo de dirección esperar de un thunk."
- ↑ ET Irons (1961-01-01). "Comentarios sobre la implementación de procedimientos y bloques recursivos en ALGOL" . Communications of the ACM . 4 (1). Association for Computing Machinery (ACM): 65– 69. doi : 10.1145/366062.366090 . ISSN 0001-0782 . S2CID 42778823 .
- ↑ Ingerman, PZ (1961-01-01). "Thunks: una forma de compilar sentencias de procedimiento con algunos comentarios sobre las declaraciones de procedimiento" . Communications of the ACM . 4 (1). Association for Computing Machinery (ACM): 55– 58. doi : 10.1145/366062.366084 . ISSN 0001-0782 . S2CID 14646332 .
- ↑ Scott, Michael (2009). Pragmática del lenguaje de programación . pág. 395.
- ↑ Marlow, Simon (2013). Programación paralela y concurrente en Haskell . pág. 10.
- ↑ Queinnec, Christian (2003). Lisp en pequeños fragmentos . pág. 176.
- ↑ Stroustrup, Bjarne (otoño de 1989). "Herencia múltiple para C++" (PDF) . Computing Systems . 1 (4). USENIX . Consultado el 4 de agosto de 2014 .
- ↑ Driesen, Karel; Hölzle, Urs (1996). "El coste directo de las llamadas a funciones virtuales en C++" (PDF) . Actas de la Conferencia ACM SIGPLAN de 1996 sobre Sistemas, Lenguajes y Aplicaciones de Programación Orientada a Objetos, OOPSLA 1996, San José, California, EE. UU., 6-10 de octubre de 1996. 11.ª OOPSLA 1996: San José, California, EE. UU. ACM . ISBN 0-89791-788-XArchivado del original (PDF) el 29/12/2019 . Consultado el 24/02/2011 .
- ↑ Calcote, John (mayo de 1995). "Thunking: Uso de bibliotecas de 16 bits en OS/2 2.0" . Revista OS/2 Developer . 7 (3): 48–56 .
- ↑ King, Adrian (1994). Inside Microsoft Windows 95 (2.ª ed.). Redmond, Washington, EE. UU.: Microsoft Press . ISBN 1-55615-626-X.
- ↑ Guía del programador para Microsoft Windows 95: Temas clave sobre programación para Windows del equipo de desarrollo de Microsoft Windows (1.ª ed.). Redmond, Washington, EE. UU.: Microsoft Press . 1 de julio de 1995. ISBN 1-55615-834-3. Consultado el 26 de mayo de 2016 .
{{cite book}}:|work=ignorado ( ayuda ) - ↑ Hazzah, Karen (1997). Escritura de controladores virtuales de dispositivos y controladores de dispositivos para Windows: secretos de programación para controladores virtuales de dispositivos (2.ª reimpresión, 2.ª ed.). Lawrence, Kansas, EE. UU.: R&D Books / Miller Freeman, Inc. ISBN 0-87930-438-3.
- ↑ Kauler, Barry (agosto de 1997). Windows Assembly Language and Systems Programming - 16- and 32-Bit Low-Level Programming for the PC and Windows (2.ª ed.). Lawrence, Kansas, EE. UU.: R&D Books / Miller Freeman, Inc. ISBN 0-87930-474-X.
- ↑ "¿Por qué no puedes pensar con claridad entre Windows de 32 bits y de 64 bits?" . The Old New Thing . 2008-10-20.
- ↑ Bright, Walter (1990-07-01). "Memoria virtual para DOS 640K" . Dr. Dobb's Journal . Recuperado el 2014-03-06 .
- ↑ Levine, John R. (2000) [octubre de 1999]. Enlazadores y cargadores . La serie Morgan Kaufmann de ingeniería de software y programación (1.ª ed.). San Francisco, EE. UU.: Morgan Kaufmann . ISBN 1-55860-496-0. OCLC 42413382 . Consultado el 12 de enero de 2020 .
{{cite book}}: CS1 maint: servicio de archivado obsoleto ( enlace ) Código: [ enlace eliminado ]Erratas: - ↑ Julliard, Alexandre. "Wine 9.0" . WineHQ Gitlab . Consultado el 10 de marzo de 2025 .
- Terminología informática
- Programación funcional