Cinema 4D performs multiple tasks in different threads. This way Cinema 4D can utilize multiple cores and can avoid that a single task blocks the application. Plugin code may be executed from the main thread or from some other thread. Depending on this thread context certain operations are not allowed. All parts of the execution and drawing pipeline of Cinema 4D are threaded.
The main thread handles the Cinema 4D GUI, user interaction and the currently active BaseDocument. It can spawn other threads like the rendering or viewport drawing thread.
The code of the following plugin classes is always executed from the main thread:
Only the main thread is allowed to modify the currently active document and to perform the following operations:
It is possible to edit the active document from an asynchronous GeDialog. Before such an edit it is needed to call StopAllThreads() since there could be other threads reading the document.
A BaseDocument is composed of several elements like generators, deformers, tags, scene hooks etc. These elements can manipulate and change the scene. For example a tag may run its "Execute" function and a generator runs its "GetVirtualObjects" function. All these functions are called in a certain order in the execution pipeline.
The scene execution can be triggered in different situations:
When a document is executed the following happens:
The order of execution depends on the priority of each element. If a plugin implements a "Execute" function the priority can be defined by implementing "AddToExecution". Many tags offer a "Priority" parameter that defines when they are executed.
Typical priority values are:
Plugin classes that offer a virtual "Execute" functions are:
After the active document is executed it is typically drawn in the editor viewport. This means that the "Draw" function of all elements is called. More information on such "Draw" functions can be found in the Draw Manual.
The execution and drawing pipeline is often executed in a thread. Other threads (like the manager redraws) access the document at the same time. That is why no scene modifications of any kind must be made in those routines. For example inserting an object into the scene is enough to trigger a crash.
As an exception, modifications are allowed that change the object's parameters like position, or anything set through NodeData::SetDParameter(). However this should only be done in Execute() by tags (expressions) and objects on EXECUTIONPRIORITY_EXPRESSION. In later stages of a generator object (e.g. EXECUTIONPRIORITY_GENERATOR) for example, this will lead to caches being rebuild constantly or mismatching data in Attribute Manager.
A common problem is to update/add materials in ObjectData::GetVirtualObjects(). This is the wrong place, materials cannot be created or changed in that function. Firstly, a redraw must never be triggered from within the redraw thread. Secondly, adding materials belongs (due to the scene access) into the user interaction part. For example, the materials needed for scene imports should be created already when the user imports the scene. ObjectData::GetVirtualObjects() is of course allowed to do ANY modifications that do not modify the scene: as the object returned is not in the scene at that time it may be changed/created in any way necessary.
Additional restrictions apply for functions that are called during rendering, e.g. ShaderData::Output() and MaterialData::CalcSurface(), since they can be run on multiple processors. No OS calls are allowed in these function.
If it is needed to modify the scene from a threaded function it is possible by sending a custom core message. Such a core message is sent with SpecialEventAdd() and can then be caught with a MessageData plugin. As MessageData::CoreMessage() runs in the context of the main tread, the active document can then be modified or GUI operations can be performed.
See the example in C4DThread Manual and Core Messages Manual.
NodeData based plugins typically become part of a BaseDocument. Their member functions are called when the document is executed and drawn. This execution can happen in a thread. An example is the "Render to Picture Viewer" process that renders a copy of the active document in the background. So one should never assume that any code of a NodeData based plugin is called in the context of the main thread.