Blender Thread/socket issue

Hi,

I’m currently work on a little app with DCC software.

When you launch the software, a socket server made in python start on a port, and he is accessible with a nodejs app.
With this, we can send python through network and with or without GUI on dcc (run on hython or mayapy for examply).

For example , when you start Maya, this server start and expose maya on a port, like 6000 for example.

This server run on another thread, to not block the main thread of dcc, in case of maya, the python received in executed on the main thread with the function “execute on main thread”.

Ok now the problem with blender:
When i send a command to blender, for example creation of a cube with this :

import bpy
bpy.ops.mesh.primitive_cube_add(size=2, enter_editmode=False, align=‘WORLD’, location=(0, 0, 0), scale=(1, 1, 1))

Our cube will be created, but blender will crash randomly with SEGMENTATION FAULT error.

To test if the server is reponsible of this crash, i have send this script to blender :

import bpy
import time
for i in range(0, 100):
bpy.ops.mesh.primitive_cube_add(size=2, enter_editmode=False, align=‘WORLD’, location=(0, 0, 0), scale=(1, 1, 1))
time.sleep(1)

This code is nicely send and received by blender, BUT, the execution of this code make crash blender randomly it can be from the first of 90th created cube.

So i don’t think the server is the reason of theses crashes.
The received code is execute on the main thread, i have try on another but is the same result.

I have try to use Asyncio library, but, it’s not async at all when you launch async code on start of blender.

I have try with Queu library, make queu of lambda and execute function periodically

bpy.app.timers.register

This not fix my issue.

I have try with qt to, because normally, qt will create a new thread for the app, but, when you launch a qt app on start of blender, it will block the main thread to…

Do you have any advice or solutions for this ?
I’m starting to lose hope with blender.

Thank you.

Using timers is the correct solution, it is required to ensure not only that the function executes on the main thread.
https://docs.blender.org/api/current/bpy.app.timers.html#use-a-timer-to-react-to-events-in-another-thread

If that doesn’t work, consider posting a simple example script that crashes. I would imagine it also crashes when adding an object every second or so.

Thank you for your reply.

Yes i have already tested this technic, but it’s doesn’t work in my case.

If you want to test, you need to make a socket server running in a class, and called on start of Blender, in /script/startup/yourSocketServer.py

 def main_server(self, host, port, connections):
        """!
        Main server entry point
        @param host String: Host ip running server
        @param port Number: Port running server
        @param connections Int: Number of connections to handle
        """
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        
        try:
            sock.bind((host, port)) 
        except Exception as socket_error:
            msg = "Socket Server, Failed to open port: {}".format(socket_error)
            logging.error(msg)
            return

        sock.listen(connections)
        logging.info("Starting Server: {}:{}".format(host, port))
        
        self.serverRunning = True
        while self.serverRunning:
            client, address = sock.accept()
            data = client.recv(1024).decode("utf-8")
            if data:

                # part for custom command like shutdown/identify etc..
                # ...
              
                else:
                    logging.info("Socket Server, Data Received: {}".format(data))
                    self.process_update(data, client)

            try:
                client.close()
            except Exception as client_error:
                logging.info("Socket Server, Error Closing Client Socket: {}".format(client_error))

        logging.info("Socket Server, Shutting Down.")

        try:
            sock.close()
        except Exception as close_error:
            logging.info("Socket Server, Error Closing Socket: {}".format(close_error))

        if data == "#Restart#":
            self.on_restart()

process update is :
def process_update(self, data, client):
“”“!
Redirect execution of data received (onMainThread for maya, for example)
@param data Json: Received Data
@param client SocketClient: Client Connection
“””
try:
print(data)
#bpy.app.handlers.frame_change_pre.append(self.function_to_process(data, client))

            self.function_to_process(data, client)
           
        except Exception as e:
            logging.error("Blender Server, Exception processing Function: {}".format(e))

So after this, function to process is executed:

def function_to_process(self, data, client):
        """
        Function to execute, received command (callback)
        @param data Json: Received Data
        @param client SocketClient: Client Connection
        """

        logging.info("Blender Server, Process Function: {}".format(data))

        global_scope = {}
        local_scope = {}
      
        try:
            exec(data, global_scope, local_scope) #

            out = local_scope.get('out', '')
            client.send(out.encode('utf-8')) # send response to the user

        except Exception as e:
             print(str(e))

The code send is just:

    import bpy
    bpy.ops.mesh.primitive_cube_add(size=2, enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(1, 1, 1))

Is send multiple time and, after random numbers of request it will crash.

Crash stacktrace :

Blender 2.91.2, Commit date: 2021-01-19 16:15, Hash 5be9ef417703

bpy.ops.mesh.primitive_cube_add(size=2, enter_editmode=False, align=‘WORLD’, location=(0, 0, 0), scale=(1, 1, 1)) # Operator
bpy.ops.mesh.primitive_cube_add(size=2, enter_editmode=False, align=‘WORLD’, location=(0, 0, 0), scale=(1, 1, 1)) # Operator
bpy.ops.mesh.primitive_cube_add(size=2, enter_editmode=False, align=‘WORLD’, location=(0, 0, 0), scale=(1, 1, 1)) # Operator
bpy.ops.mesh.primitive_cube_add(size=2, enter_editmode=False, align=‘WORLD’, location=(0, 0, 0), scale=(1, 1, 1)) # Operator
bpy.ops.mesh.primitive_cube_add(size=2, enter_editmode=False, align=‘WORLD’, location=(0, 0, 0), scale=(1, 1, 1)) # Operator
bpy.ops.mesh.primitive_cube_add(size=2, enter_editmode=False, align=‘WORLD’, location=(0, 0, 0), scale=(1, 1, 1)) # Operator
bpy.ops.mesh.primitive_cube_add(size=2, enter_editmode=False, align=‘WORLD’, location=(0, 0, 0), scale=(1, 1, 1)) # Operator
bpy.ops.mesh.primitive_cube_add(size=2, enter_editmode=False, align=‘WORLD’, location=(0, 0, 0), scale=(1, 1, 1)) # Operator
bpy.ops.mesh.primitive_cube_add(size=2, enter_editmode=False, align=‘WORLD’, location=(0, 0, 0), scale=(1, 1, 1)) # Operator
bpy.ops.mesh.primitive_cube_add(size=2, enter_editmode=False, align=‘WORLD’, location=(0, 0, 0), scale=(1, 1, 1)) # Operator
bpy.ops.mesh.primitive_cube_add(size=2, enter_editmode=False, align=‘WORLD’, location=(0, 0, 0), scale=(1, 1, 1)) # Operator

backtrace

./blender(BLI_system_backtrace+0x20) [0x84bbb90]
./blender() [0xe529bd]
/lib/x86_64-linux-gnu/libc.so.6(+0x46210) [0x7f7b766de210]
./blender(_ZN7blender3deg24DepsgraphRelationBuilder29build_copy_on_write_relationsEPNS0_6IDNodeE+0x19c) [0x70ffc8c]
./blender(_ZN7blender3deg24DepsgraphRelationBuilder29build_copy_on_write_relationsEv+0x34) [0x70fe0d4]
./blender(_ZN7blender3deg23AbstractBuilderPipeline20build_step_relationsEv+0x3e) [0x710be5e]
./blender(_ZN7blender3deg23AbstractBuilderPipeline5buildEv+0x4a) [0x710bf5a]
./blender(DEG_graph_build_from_view_layer+0x20) [0x70f5d70]
./blender() [0xf1d675]
./blender(wm_event_do_depsgraph+0xab) [0x11c336b]
./blender(wm_event_do_refresh_wm_and_depsgraph+0x87) [0x11c34a7]
./blender(wm_event_do_notifiers+0x34a) [0x11c9c8a]
./blender(WM_main+0x28) [0x11bf868]
./blender(main+0x30d) [0xd8d1ad]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0x7f7b766bf0b3]
./blender() [0xe4f133]

Python backtrace

Thank you.

I don’t see timers used in any code you posted? I would not expect it to work without that.

Yes because read queue every 0.1s for example block the main thread, so i have share my current code, not my tested code.

ok so there is my read queue code with your timers :

def readQueu():
    item = q.get()
    if item:
        item()
        print("exec")
        q.task_done()
    return 0.1

bpy.app.timers.register(readQueu) 

blender is totally freeze with this because i think is trigger by the main thread.

And the “put” method is in process_update method :

def process_update(self, data, client):
        """!
        Redirect execution of data received (onMainThread for maya, for example)
        @param data Json: Received Data
        @param client SocketClient: Client Connection
        """
        try:
            q.put(lambda: self.function_to_process(data, client))

        except Exception as e:
            logging.error("Blender Server, Exception processing Function: {}".format(e)) 

edit : After many test, i think, is not crash anymore but, blender is unusable and totally freeze for the users

Thank you

q.put(lambda: self.function_to_process(data, client))

This puts a function in the queue that does networking, and so then you will be doing networking on the main thread. The point of the queue would be to put only the Blender operations in there.

So only the string and pass it to the queue, and in the exec, this python string ?

If you have a little example of code, that i understand, i would be grateful.

Thank you again

Edit : It’s not rather reading the queue that blocks the main thread?

Sorry to bring up this topic, but someone else has some info?