A thinker is an instance of a large number of diverse structures in the Doom engine which are used to implement per-tic scheduled actions within the game world. Thinkers are linked into a global double-linked list which is iterated down once per tic, giving each object a turn at running its logic for that time slice. This is a very basic implementation of a cooperative multitasking system.
Types of thinkers
The following types of objects in the game world are implemented as thinkers in the original game engine:
- Moving floors and ceilings: doors, lifts, crushing ceilings, etc.
- Map objects
- Sector lighting effects
Hexen adds many new types of thinkers in its customization of the Doom engine:
Source ports may significantly expand the scope of thinkers to include new effects.
Though Doom was written in the C language, which is not inherently object oriented, thinkers utilize a technique common in C programming called pseudo-inheritance. Every type of structure which behaves as a thinker contains a thinker_t instance as the first member in its definition. This allows pointers to any of the various descendant types, such as mobj_t, to be cast to and from a pointer to thinker_t. Each thinker contains a pointer to a function to be executed every tic that is specific to its derived type, such as P_MobjThinker for mobj_t. Testing the value of this function to determine the true type of a thinker_t instance is used as a rudimentary form of run-time type information.
The original engine does not implement any form of reference counting, and therefore it is possible for objects which have been removed from the list and freed to be referenced or modified afterward, resulting in arbitrary or "undefined" behavior, which may lead to corruption of other thinkers or the thinker list itself, and may eventually lead to a crash. Lost souls are particularly prone to issues related to this problem because when they become invisible at the end of their death sequence, they free themselves, causing any monsters still angry at them to possibly continue to reference invalid memory.
The Boom source port was the first implementation to correct this flaw via addition of reference counting. Some later ports have replaced the reference counting scheme with full-scale garbage collection.
Language standard conflicts
The type punning scheme used to cast structure pointers back to their true type from thinker_t * is illegal in the current C11 language standard. This means that modern C language compilers may not generate valid code given the Doom source code unless special command line switches are provided (such as -fno-strict-aliasing for GCC). This is true in particular when high levels of optimization are requested from the compiler.