<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Deadlock: TakeData.SetCurrentTake() from Python holds the GIL]]></title><description><![CDATA[<p dir="auto">Hello. I found some strange issues with C4D freezing when developing a tool to manage Takes.<br />
I tried to debug it with Claude and here is the report:</p>
<hr />
<p dir="auto"><strong>Product:</strong> Cinema 4D 2027.0.0 (Python 3.11.4), Windows 11 Pro. Redshift is the active renderer, but the repro does not depend on it.</p>
<h2>Summary</h2>
<p dir="auto"><code>c4d.modules.takesystem.TakeData.SetCurrentTake()</code> called from Python on the main thread does not release the GIL. While the take switch runs, Cinema 4D waits for a viewport redraw (or scene evaluation) running in another thread. If that thread evaluates Python (a Python tag, an XPresso Python node, or any other Python-based scene element), it waits for the GIL held by the main thread. Cinema 4D stops responding and never recovers; the process has to be killed.</p>
<p dir="auto">The same switch made from C++ with the GIL released around the call never hangs. This suggests the Python wrapper should release the GIL for the duration of the call (<code>Py_BEGIN_ALLOW_THREADS</code> / <code>Py_END_ALLOW_THREADS</code>), as <code>c4d.CallCommand</code> apparently already does.</p>
<h2>Steps to reproduce</h2>
<ol>
<li>Start Cinema 4D 2027 with the default layout (one perspective view is enough).</li>
<li>Run the attached <code>take_switch_gil_deadlock_repro.py</code> from the Script Manager. The script:
<ul>
<li>creates a new document with one cube that carries a Python tag, whose <code>main()</code> takes about 20 ms (standing for a heavy rig);</li>
<li>adds one child Take that overrides the cube's position;</li>
<li>opens an async <code>GeDialog</code> whose <code>Timer()</code> switches between the Main Take and the child Take every 50 ms with <code>TakeData.SetCurrentTake()</code> followed by <code>c4d.EventAdd()</code>.</li>
</ul>
</li>
</ol>
<p dir="auto"><strong>Expected:</strong> the dialog keeps switching Takes.</p>
<p dir="auto"><strong>Actual:</strong> Cinema 4D stops responding at the first or one of the first switches and stays frozen. In our test it hung at the very first switch; py-spy shows the main thread at <code>td.SetCurrentTake(target)</code>.</p>
<p dir="auto">With a Python tag whose <code>main()</code> is just <code>pass</code>, the window is very small and 1000 switches usually pass; the 20 ms tag only makes the race reproducible. In production we hit it with a character rig whose Python tag lives inside an XRef, by clicking in a Python dialog that switches Takes.</p>
<h2>Stacks of the hung process (py-spy dump --native)</h2>
<p dir="auto">Main thread, holding the GIL, waiting inside the take switch:</p>
<pre><code>Thread 29216 (idle): "MainThread"
    ZwWaitForAlertByThreadId (ntdll.dll)
    RtlSleepConditionVariableCS (ntdll.dll)
    SleepConditionVariableCS (KERNELBASE.dll)
    (Cinema 4D.exe)
    (Cinema 4D.exe)
    (c4d_base.xdl64)
    (c4d_base.xdl64)
    (c4d_base.xdl64)
    (c4d_manager.xdl64)
    (python.xdl64)                      &lt;- TakeData.SetCurrentTake wrapper
    PyType_GenericNew (python311.dll)
    PyObject_Vectorcall (python311.dll)
    PyEval_EvalFrameDefault (python311.dll)
    Timer (take_switch_gil_deadlock_repro.py:69)   &lt;- td.SetCurrentTake(target)
</code></pre>
<p dir="auto">Redraw / evaluation thread, inside the Python tag, waiting for the GIL:</p>
<pre><code>Thread 28012 (idle)
    NtWaitForSingleObject (ntdll.dll)
    WaitForSingleObjectEx (KERNELBASE.dll)
    PyObject_Call_Prepend (python311.dll)
    PyEval_RestoreThread (python311.dll)      &lt;- waits for the GIL
    ...
    PyObject_CallFunction (python311.dll)
    (python.xdl64)
    (c4d_base.xdl64)
    (Cinema 4D.exe)
    main (Python tag, line 5)
</code></pre>
<p dir="auto">In production the main thread variant was <code>SetCurrentTake -&gt; c4d_manager -&gt; c4d_base -&gt; hair.xdl64 -&gt; ca.xdl64 -&gt; c4d_base -&gt; SwitchToThread</code> (a spin wait). The other thread was a job thread under <code>ca.xdl64</code> in <code>PyGILState_Ensure</code>, about to run the rig's Python tag. In the reproducible cases <code>CHECKISRUNNING_VIEWDRAWING</code> was 1 at the time of the switch.</p>
<h2>What we measured</h2>
<ul>
<li>Switching Takes from Python every 50 ms with <code>EventAdd()</code> between the switches, in a scene with a rig whose Python tag lives inside an XRef: hang at the 13th switch.</li>
<li>The same loop, but with the switch made by a C++ <code>CommandData</code> called through <code>c4d.CallCommand()</code>, which also releases the GIL (<code>PyEval_SaveThread</code> / <code>PyEval_RestoreThread</code>) around <code>TakeData::SetCurrentTake()</code>: 1000 switches, 780 of them while a redraw was running, no hang.</li>
<li>300 switches in a single Python call without <code>EventAdd()</code>, so no redraw can start in between: no hang. The deadlock needs a redraw that is already running when the switch starts.</li>
<li><code>c4d.StopAllThreads()</code> does not stop a running viewport redraw: it returns immediately and <code>CHECKISRUNNING_VIEWDRAWING</code> stays 1.</li>
<li><code>c4d.EventAdd()</code> does not start a redraw while the main thread is still in Python. A redraw started between two Python calls (e.g. two <code>Timer()</code> ticks) keeps running into the next one.</li>
<li>An undoable parameter edit in the same setup (<code>StartUndo</code> / <code>AddUndo(UNDOTYPE_CHANGE)</code> / parameter change / <code>EndUndo</code> every 50 ms): 1000 edits, no hang.</li>
<li>We did, however, observe the same kind of hang in production in two more calls, both while a viewport redraw was evaluating a Python tag. We could not reproduce them in a minimal scene.
<ul>
<li><code>BaseDocument.AddUndo()</code>: main thread in <code>AddUndo -&gt; hair -&gt; ca -&gt; SwitchToThread</code>.</li>
<li><code>maxon.GraphTransaction.Commit()</code>: main thread on a condition variable, a drawport thread in <code>PyGILState_Ensure</code>.</li>
</ul>
</li>
</ul>
<h2>Workarounds we use</h2>
<ol>
<li>Before a scene write from Python, wait until <code>c4d.CheckIsRunning(c4d.CHECKISRUNNING_VIEWDRAWING)</code> has stayed 0 for about 10 ms, sleeping in between (<code>time.sleep</code> releases the GIL so the redraw can finish). One redraw runs in parts with the flag off for a moment between them, so a single check is not enough. This narrows the window but cannot close it.</li>
<li>Make the Take switch from a small C++ command plugin that releases the GIL around <code>TakeData::SetCurrentTake()</code>. Python passes the Take through the world container and calls <code>c4d.CallCommand()</code>. This removes the deadlock, but every Python tool would need such a native helper.</li>
</ol>
<h2>Questions / request</h2>
<ol>
<li>Could the Python wrappers of <code>TakeData.SetCurrentTake()</code> release the GIL while the native call runs? The same applies to other calls that can wait for scene evaluation or redraws, e.g. <code>BaseDocument.AddUndo()</code>, <code>GraphTransaction.Commit()</code> and <code>BaseDocument.ExecutePasses()</code>.</li>
<li>Is there a supported way for a Python plugin to block or pause viewport redraws for the duration of a set of scene changes? Or to wait until no redraw or scene evaluation is running, something stronger than polling <code>CHECKISRUNNING_VIEWDRAWING</code>?</li>
<li>Is calling <code>TakeData.SetCurrentTake()</code> from a <code>GeDialog.Timer()</code> or a <code>GeUserArea</code> mouse handler supported, or should it be deferred to some other context?</li>
</ol>
<p dir="auto"><a href="/forum/assets/uploads/files/1794233348932-take_switch_gil_deadlock_repro.py">take_switch_gil_deadlock_repro.py</a></p>
]]></description><link>http://developers.maxon.net/forum/topic/16457/deadlock-takedata.setcurrenttake-from-python-holds-the-gil</link><generator>RSS for Node</generator><lastBuildDate>Tue, 10 Nov 2026 16:04:15 GMT</lastBuildDate><atom:link href="http://developers.maxon.net/forum/topic/16457.rss" rel="self" type="application/rss+xml"/><pubDate>Mon, 09 Nov 2026 14:12:05 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to Deadlock: TakeData.SetCurrentTake() from Python holds the GIL on Tue, 10 Nov 2026 14:41:37 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/forum/user/nik684">@<bdi>nik684</bdi></a> said in <a href="/forum/post/77395">Deadlock: TakeData.SetCurrentTake() from Python holds the GIL</a>:</p>
<blockquote>
<p dir="auto">Also worth to note:<br />
We hit the same freeze in BaseDocument.AddUndo() and maxon.GraphTransaction.Commit() on the main thread while a Python tag was drawing.<br />
py-spy showed the same pattern: the main thread holds the GIL and waits, and the draw/ca thread sits in PyGILState_Ensure.<br />
Could those wrappers be missing the GIL release too?</p>
<p dir="auto">Undo maybe be related to Take switch though.</p>
</blockquote>
<p dir="auto">AddUndo just as some other Undo methods does not release the GIL, I just checked. But other than for the take system, this could be intentional here, due to adding an undo could imply changes to <code>PyObject</code> instances (which are not allowed while you drop the GIL).</p>
<p dir="auto">For <code>maxon.GraphTransaction.Commit()</code> I would have to check, things work entirely different there, the Maxon API is not implemented in a traditional way, things work much differently there. I would need code and a context for the latter. For the former I probably need a good night sleep to think about it and a reason where this would be relevant.</p>
<p dir="auto">Cheers,<br />
Ferdinand</p>
]]></description><link>http://developers.maxon.net/forum/post/77397</link><guid isPermaLink="true">http://developers.maxon.net/forum/post/77397</guid><dc:creator><![CDATA[ferdinand]]></dc:creator><pubDate>Tue, 10 Nov 2026 14:41:37 GMT</pubDate></item><item><title><![CDATA[Reply to Deadlock: TakeData.SetCurrentTake() from Python holds the GIL on Tue, 10 Nov 2026 14:34:56 GMT]]></title><description><![CDATA[<p dir="auto">You picked a terrible spot to start your Cinema journey, the take system while beloved by users, is both complicated and not performance-wise not great when it comes to the API <img src="http://developers.maxon.net/forum/assets/plugins/nodebb-plugin-emoji/emoji/android/1f604.png?v=0b8ddba251d" class="not-responsive emoji emoji-android emoji--smile" style="height:23px;width:auto;vertical-align:middle" title=":D" alt="😄" /></p>
<p dir="auto">That is why I added my <strong>"It can entail a lot of data copying and scene traversal to bring the current scene into its new state"</strong> warning above. The take system entails a lot of copying of data containers or at least parts of them. The data itself is probably only a small portion of the spike you see, what makes this then really expensive, is the fact that when you make half your scene dirty by applying new parameters to it, you also have to rebuild half your scene from scratch which eats a lot of memory.</p>
<p dir="auto">But I cannot really do anything about this, as this is not my domain, I do not own the take system, just the Python bindings to it. When you consider the take system too slow, I would encourage you to make a user report in our <a href="https://www.maxon.net/en/support-center" target="_blank" rel="noopener noreferrer nofollow ugc">Support Center</a>. This is also probably not something you fix in five minutes.</p>
<p dir="auto">Cheers,<br />
Ferdinand</p>
]]></description><link>http://developers.maxon.net/forum/post/77396</link><guid isPermaLink="true">http://developers.maxon.net/forum/post/77396</guid><dc:creator><![CDATA[ferdinand]]></dc:creator><pubDate>Tue, 10 Nov 2026 14:34:56 GMT</pubDate></item><item><title><![CDATA[Reply to Deadlock: TakeData.SetCurrentTake() from Python holds the GIL on Tue, 10 Nov 2026 14:25:35 GMT]]></title><description><![CDATA[<p dir="auto">Also worth to note:<br />
We hit the same freeze in BaseDocument.AddUndo() and maxon.GraphTransaction.Commit() on the main thread while a Python tag was drawing.<br />
py-spy showed the same pattern: the main thread holds the GIL and waits, and the draw/ca thread sits in PyGILState_Ensure.<br />
Could those wrappers be missing the GIL release too?</p>
<p dir="auto">Undo maybe be related to Take switch though.</p>
]]></description><link>http://developers.maxon.net/forum/post/77395</link><guid isPermaLink="true">http://developers.maxon.net/forum/post/77395</guid><dc:creator><![CDATA[nik684]]></dc:creator><pubDate>Tue, 10 Nov 2026 14:25:35 GMT</pubDate></item><item><title><![CDATA[Reply to Deadlock: TakeData.SetCurrentTake() from Python holds the GIL on Tue, 10 Nov 2026 14:17:09 GMT]]></title><description><![CDATA[<p dir="auto">Wow, first of all I'm impressed by quality and speed of this support! I'm newbie here so it was a pleasant surprise. As a maxscript guy I'm not used to this <img src="http://developers.maxon.net/forum/assets/plugins/nodebb-plugin-emoji/emoji/android/1f642.png?v=0b8ddba251d" class="not-responsive emoji emoji-android emoji--slightly_smiling_face" style="height:23px;width:auto;vertical-align:middle" title=":)" alt="🙂" /><br />
I'll study every link you posted and for sure will tweak the agent to be more concise in future.<br />
Thanks a lot for the upcoming fix.</p>
<p dir="auto">Regarding test script. I guess that script was written intentionally stressfull by Claude to reproduce the issue. Ofc I'm not spamming Take switch that frequently in the production tool, but I got freezes even with pretty calm usual switches. Now we know why.</p>
<p dir="auto">Taking advantage of the moment, I would like to mention that Override Groups are pretty greedy in terms of memory for some reason. When I put a lot of objects in Override Group Cinema takes much more RAM than when I'm using usual parameter overrides with the same amounf of objects/parameters. It doesn't look like a bug, but maybe there is some room for improvement. Should I create another post with this issue?</p>
]]></description><link>http://developers.maxon.net/forum/post/77394</link><guid isPermaLink="true">http://developers.maxon.net/forum/post/77394</guid><dc:creator><![CDATA[nik684]]></dc:creator><pubDate>Tue, 10 Nov 2026 14:17:09 GMT</pubDate></item><item><title><![CDATA[Reply to Deadlock: TakeData.SetCurrentTake() from Python holds the GIL on Tue, 10 Nov 2026 13:30:12 GMT]]></title><description><![CDATA[<p dir="auto">Okay, this did fix it. I made a merge request. It will <strong>not</strong> be included in the next release (which will arrive very soon). I hope to get it into the 2027 Q1 release (which is also already on the finish line), otherwise it will come with Q2. I also fixed some other methods in take take system, as quite a few were missing proper GIL handling.</p>
<p dir="auto"><video class="embed-video-local" controls src="/forum/assets/uploads/files/1794317216478-test.mp4">
</video></p>]]></description><link>http://developers.maxon.net/forum/post/77393</link><guid isPermaLink="true">http://developers.maxon.net/forum/post/77393</guid><dc:creator><![CDATA[ferdinand]]></dc:creator><pubDate>Tue, 10 Nov 2026 13:30:12 GMT</pubDate></item><item><title><![CDATA[Reply to Deadlock: TakeData.SetCurrentTake() from Python holds the GIL on Tue, 10 Nov 2026 11:40:47 GMT]]></title><description><![CDATA[<p dir="auto">Hello <a class="plugin-mentions-user plugin-mentions-a" href="/forum/user/nik684">@<bdi>nik684</bdi></a>,</p>
<p dir="auto">Welcome to the Maxon developers forum and its community, it is great to have you with us!</p>
<h4>Getting Started</h4>
<p dir="auto">Before creating your next postings, we would recommend making yourself accustomed with our forum and support procedures. You did not do anything wrong, we point all new users to these rules.</p>
<ul>
<li><a href="/forum/topic/15242/">Forum Overview</a>: Provides a broad overview of the fundamental structure and rules of this forum, such as the purpose of the different sub-forums or the fact that we will ban users who engage in hate speech or harassment.</li>
<li><a href="/forum/topic/15244/">Support Procedures</a>: Provides a more in detail overview of how we provide technical support for APIs here. This topic will tell you how to ask good questions and limits of our technical support.</li>
<li><a href="/forum/topic/15243/">Forum Features</a>: Provides an overview of the technical features of this forum, such as Markdown markup or file uploads.</li>
</ul>
<p dir="auto">It is <strong>strongly</strong> recommended to read the first two topics carefully, especially the section <a href="/forum/topic/15244/support-procedures/4">Support Procedures: How to Ask Questions</a>.</p>
<h4>About your First Question</h4>
<p dir="auto">This has to be one of the most thorough bug reports I have ever seen. Well done! Maybe tell your agent next time to make it a bit shorter, I have a not too long attention span <img src="http://developers.maxon.net/forum/assets/plugins/nodebb-plugin-emoji/emoji/android/1f604.png?v=0b8ddba251d" class="not-responsive emoji emoji-android emoji--smile" style="height:23px;width:auto;vertical-align:middle" title=":D" alt="😄" /></p>
<blockquote>
<p dir="auto">Could the Python wrappers of TakeData.SetCurrentTake() release the GIL while the native call runs ?</p>
</blockquote>
<p dir="auto">You were right, <code>CPyTakeData::SetCurrentTake</code> was missing the GIL release and acquire around its <code>takeData-&gt;SetCurrentTake(take)</code> call as it is customary for methods that write something. I have just pushed a build to the build server which changes that. I will then try to run your script there once I have that build. And even when the fix does not resolve your issue, I will submit it anyway, as the GIL should be released by convention around that call I would say.</p>
<blockquote>
<p dir="auto">Is there a supported way for a Python plugin to block or pause viewport redraws for the duration of a set of scene changes?</p>
</blockquote>
<p dir="auto">No, there is unfortunately not. You are in a more or less curated threading environment in Python. The Python VM calls your code at specific points in time and you are more or less at its mercy. There are <code>c4d.StopAllThreads</code> and <code>c4d.threading.GeThreadLock</code> but using them is rarely a good idea. And some drawing threads might even ignore them.</p>
<blockquote>
<p dir="auto">Is calling TakeData.SetCurrentTake() from a GeDialog.Timer() or a GeUserArea mouse handler supported, or should it be deferred to some other context?</p>
</blockquote>
<p dir="auto">That is indeed the main question. You can look at the all new <a href="https://developers.maxon.net/docs/py/2027_0_0/manuals/manual_scene_execution.html" target="_blank" rel="noopener noreferrer nofollow ugc">Scene Excecution</a> manual which I wrote for 2027 to get a better understanding of this. In short, changing the active take of a loaded scene is a scene modification and therefore must be main thread bound. Timer and Mouse/Keyboard IO functions usually run on the MT, but I would add an explicit check to such functions just to me sure. So, on paper, this is fine.</p>
<p dir="auto">Where I can see a bit an issue is with your <code>INTERVAL_MS</code> in the demo script you gave us, it is set to 50ms. Quoting the warning I once added to <code>GetTimer</code>:</p>
<p dir="auto"><em>"Depending on the speed of the computer, the operating system, the complexity of the dialog and the threads running in the background, there is no guarantee that event messages will occur on a regular basis. Using a value of 500 ms should be no problem but if using a value of 1 ms one might get events with the following time spaces: 3 ms, 76 ms, 15 ms, 19 ms, 67 ms…"</em></p>
<p dir="auto">With 50ms you are dangerously close to saturating the event system. Although it might not look like it, <code>SetCurrentTake</code> is a VERY expensive call. Doing this every 50ms with all Python shenanigans in between is not great. You would be better served with a value such as 100 or even 250ms.</p>
<p dir="auto">I am like 66% confident that the GIL release I added will fix your issue. But all that event and timer stuff could also throw a wrench into things. But I will cross that bridge once I have a build of my fix, and your code still freezes, as that is probably not fun to debug. <strong>You should take as a lesson here that writing takes is not cheap. It can entail a lot of data copying and scene traversal to bring the current scene into its new state.</strong></p>
<p dir="auto">I will post here an update once I have a final answer.</p>
<p dir="auto">Cheers,<br />
Ferdinand</p>
]]></description><link>http://developers.maxon.net/forum/post/77392</link><guid isPermaLink="true">http://developers.maxon.net/forum/post/77392</guid><dc:creator><![CDATA[ferdinand]]></dc:creator><pubDate>Tue, 10 Nov 2026 11:40:47 GMT</pubDate></item></channel></rss>