API doc: info_gotcha: extend description of issues related to memory changes.
* Added a TL;DR first paragraph summarizing that one shall not keep any reference to Blender data when modifying its container. * Added some info about fact that adding items to some data containers (like Collection) can also invalidate existing items (due to array re-allocation). * Added a Do/Don't example which shows a crash after adding some items to a collection. Related to T61297.
This commit is contained in:
parent
9299073d57
commit
49c100e56c
@ -604,12 +604,17 @@ so until its properly supported, best not make use of this.
|
||||
Help! My script crashes Blender
|
||||
===============================
|
||||
|
||||
**TL;DR:** Do not keep direct references to Blender data (of any kind) when modifying the container
|
||||
of that data, and/or when some undo/redo may happen (e.g. during modal operators execution...).
|
||||
Instead, use indices (or other data always stored by value in Python, like string keys...),
|
||||
that allow you to get access to the desired data.
|
||||
|
||||
Ideally it would be impossible to crash Blender from Python
|
||||
however there are some problems with the API where it can be made to crash.
|
||||
|
||||
Strictly speaking this is a bug in the API but fixing it would mean adding memory verification
|
||||
on every access since most crashes are caused by the Python objects referencing Blenders memory directly,
|
||||
whenever the memory is freed, further Python access to it can crash the script.
|
||||
whenever the memory is freed or re-allocated, further Python access to it can crash the script.
|
||||
But fixing this would make the scripts run very slow,
|
||||
or writing a very different kind of API which doesn't reference the memory directly.
|
||||
|
||||
@ -619,10 +624,15 @@ Here are some general hints to avoid running into these problems.
|
||||
especially when working with large lists since Blender can crash simply by running out of memory.
|
||||
- Many hard to fix crashes end up being because of referencing freed data,
|
||||
when removing data be sure not to hold any references to it.
|
||||
- Re-allocation can lead to the same issues
|
||||
(e.g. if you add a lot of items to some Collection,
|
||||
this can lead to re-allocating the underlying container's memory,
|
||||
invalidating all previous references to existing items).
|
||||
- Modules or classes that remain active while Blender is used,
|
||||
should not hold references to data the user may remove, instead,
|
||||
fetch data from the context each time the script is activated.
|
||||
- Crashes may not happen every time, they may happen more on some configurations/operating-systems.
|
||||
- Be wary of recursive patterns, those are very efficient at hiding the issues described here.
|
||||
|
||||
.. note::
|
||||
|
||||
@ -632,6 +642,55 @@ Here are some general hints to avoid running into these problems.
|
||||
While the crash may be in Blenders C/C++ code,
|
||||
this can help a lot to track down the area of the script that causes the crash.
|
||||
|
||||
.. note::
|
||||
|
||||
Some container modifications are actually safe, because they will never re-allocate existing data
|
||||
(e.g. linked lists containers will never re-allocate existing items when adding or removing others).
|
||||
|
||||
But knowing which cases are safe and which aren't implies a deep understanding of Blender's internals.
|
||||
That's why, unless you are willing to dive into the RNA C implementation, it's simpler to
|
||||
always assume that data references will become invalid when modifying their containers,
|
||||
in any possible way.
|
||||
|
||||
|
||||
**Don’t:**
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class TestItems(bpy.types.PropertyGroup):
|
||||
name: bpy.props.StringProperty()
|
||||
|
||||
bpy.utils.register_class(TestItems)
|
||||
bpy.types.Scene.test_items = bpy.props.CollectionProperty(type=TestItems)
|
||||
|
||||
first_item = bpy.context.scene.test_items.add()
|
||||
for i in range(100):
|
||||
bpy.context.scene.test_items.add()
|
||||
|
||||
# This is likely to crash, as internal code may re-allocate
|
||||
# the whole container (the collection) memory at some point.
|
||||
first_item.name = "foobar"
|
||||
|
||||
|
||||
**Do:**
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class TestItems(bpy.types.PropertyGroup):
|
||||
name: bpy.props.StringProperty()
|
||||
|
||||
bpy.utils.register_class(TestItems)
|
||||
bpy.types.Scene.test_items = bpy.props.CollectionProperty(type=TestItems)
|
||||
|
||||
first_item = bpy.context.scene.test_items.add()
|
||||
for i in range(100):
|
||||
bpy.context.scene.test_items.add()
|
||||
|
||||
# This is safe, we are getting again desired data *after*
|
||||
# all modifications to its container are done.
|
||||
first_item = bpy.context.scene.test_items[0]
|
||||
first_item.name = "foobar"
|
||||
|
||||
|
||||
Undo/Redo
|
||||
---------
|
||||
|
Loading…
Reference in New Issue
Block a user