Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issues with using threads on an iOS device #609

Closed
jda5 opened this issue Apr 13, 2021 · 4 comments
Closed

Issues with using threads on an iOS device #609

jda5 opened this issue Apr 13, 2021 · 4 comments

Comments

@jda5
Copy link

jda5 commented Apr 13, 2021

Versions

  • Python : 3.9
  • MacOS version : 10.15.7
  • XCode Version : 12.3
  • Cython version : 0.29.17

Describe the bug

I have built an app that makes use of the Python threading module to sent POST requests to an API. The information returned then causes changes to the properties of certain widgets. The issue is that using threads sometimes results in parts of the UI being called from a background thread (i.e. not the main thread), which crashes the app. The Xcode logs throw out the following exception:

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'main thread only'

To Reproduce

Below is the code I use to make the requests:

import json
import requests
import base64
import threading

def thread(function):
    def wrap(*args, **kwargs):
        t = threading.Thread(target=function, args=args, kwargs=kwargs)
        t.start()
        return t
    return wrap


class MathPixAPI:

    stroke_url = '*******************'  # information has been censored
    header = {
        "content-type": "application/json",
        "app_id": "*******************",
        "app_key": "*******************"
    }

    @thread
    def post_data(self, file_name: str, root):
        """
        Posts a base64 encoded image to the MathPixAPI then updates the data DictProperty of the ExpressionWriter that
        calls this function
        :param file_name: The name of the file - e.g. "image.png"
        :param root: The ExpressionWriter that calls the function
        """
        image_uri = "data:image/png;base64," + base64.b64encode(open(file_name, "rb").read()).decode()
        r = requests.post("https://api.mathpix.com/v3/text",
                          data=json.dumps({'src': image_uri}),
                          headers=self.header)
        root.data = json.loads(r.text)

The app makes no more than 5 asynchronous requests at one time, and is called from the function below:

def get_image_data(self):
        """
        The function first saves the ExpressionWriter.canvas as a PNG file to the user_data_directory (automatically
        determined depending on the device the user is running the app on). Then this images is sent to the MathPix API
        which then return data on the handwritten answer (see api.py for more details). The api call updates self.data
        which in turn calls self._on_data().
        """
        file_name = f'{App.get_running_app().user_data_dir}/image_{self.number}.png'
        self.export_to_png(file_name)
        MathPixAPI().post_data(file_name, self)

Expected behavior

Unusually, there seems to be no obvious cause for this. Most of the time the threads work fine, but about one in every ten requests result in the program crashing.

Logs

2021-04-13 12:23:16.735763+0100 ccc-writer-3[5609:5183173] [Animation] +[UIView setAnimationsEnabled:] being called from a background thread. Performing any operation from a background thread on UIView or a subclass is not supported and may result in unexpected and insidious behavior. trace=(
	0   UIKitCore                           0x0000000187cbb538 8518EAE3-832B-3FF0-9FA5-9DBE3041F26C + 17859896
	1   libdispatch.dylib                   0x0000000103b596c0 _dispatch_client_callout + 20
	2   libdispatch.dylib                   0x0000000103b5b1f8 _dispatch_once_callout + 136
	3   UIKitCore                           0x0000000187cbb4bc 8518EAE3-832B-3FF0-9FA5-9DBE3041F26C + 17859772
	4   UIKitCore                           0x0000000187cbb628 8518EAE3-832B-3FF0-9FA5-9DBE3041F26C + 17860136
	5   UIKitCore                           0x0000000187abbd64 8518EAE3-832B-3FF0-9FA5-9DBE3041F26C + 15764836
	6   UIKitCore                           0x0000000187aae150 8518EAE3-832B-3FF0-9FA5-9DBE3041F26C + 15708496
	7   UIKitCore                           0x00000001877b2f20 8518EAE3-832B-3FF0-9FA5-9DBE3041F26C + 12582688
	8   UIKitCore                           0x0000000187cb2b30 8518EAE3-832B-3FF0-9FA5-9DBE3041F26C + 17824560
	9   UIKitCore                           0x0000000187aacd50 8518EAE3-832B-3FF0-9FA5-9DBE3041F26C + 15703376
	10  ccc-writer-3                        0x00000001027ee960 -[SDL_uikitviewcontroller showKeyboard] + 108
	11  ccc-writer-3                        0x00000001027ef164 UIKit_ShowScreenKeyboard + 60
	12  ccc-writer-3                        0x00000001027b8490 SDL_StartTextInput + 92
	13  ccc-writer-3                        0x000000010289f0b8 __pyx_pw_4kivy_4core_6window_12_window_sdl2_18_WindowSDL2Storage_57show_keyboard + 3544
	14  ccc-writer-3                        0x000000010252280c method_vectorcall_VARARGS_KEYWORDS + 348
	15  ccc-writer-3                        0x00000001025e0e6c call_function + 288
	16  ccc-writer-3                        0x00000001025dcdf4 _PyEval_EvalFrameDefault + 5432
	17  ccc-writer-3                        0x00000001025e1e98 _PyEval_EvalCodeWithName + 3212
	18  ccc-writer-3                        0x000000010251a160 _PyFunction_Vectorcall + 248
	19  ccc-writer-3                        0x000000010251c58c method_vectorcall + 156
	20  ccc-writer-3                        0x00000001025e0e6c call_function + 288
	21  ccc-writer-3                        0x00000001025ddd50 _PyEval_EvalFrameDefault + 9364
	22  ccc-writer-3                        0x0000000102519fe0 function_code_fastcall + 120
	23  ccc-writer-3                        0x00000001025e0e6c call_function + 288
	24  ccc-writer-3                        0x00000001025dcdf4 _PyEval_EvalFrameDefault + 5432
	25  ccc-writer-3                        0x0000000102519fe0 function_code_fastcall + 120
	26  ccc-writer-3                        0x00000001025e0e6c call_function + 288
	27  ccc-writer-3                        0x00000001025dcdf4 _PyEval_EvalFrameDefault + 5432
	28  ccc-writer-3                        0x00000001025e1e98 _PyEval_EvalCodeWithName + 3212
	29  ccc-writer-3                        0x000000010251a160 _PyFunction_Vectorcall + 248
	30  ccc-writer-3                        0x000000010251c664 method_vectorcall + 372
	31  ccc-writer-3                        0x000000010251995c PyVectorcall_Call + 104
	32  ccc-writer-3                        0x00000001025de888 _PyEval_EvalFrameDefault + 12236
	33  ccc-writer-3                        0x00000001025e1e98 _PyEval_EvalCodeWithName + 3212
	34  ccc-writer-3                        0x00000001025db87c PyEval_EvalCodeEx + 72
	35  ccc-writer-3                        0x0000000102825f50 __Pyx_PyFunction_FastCallDict + 256
	36  ccc-writer-3                        0x000000010282a5dc __pyx_f_4kivy_6_event_14EventObservers__dispatch + 3020
	37  ccc-writer-3                        0x000000010282b020 __pyx_f_4kivy_6_event_14EventObservers_dispatch + 624
	38  ccc-writer-3                        0x0000000102b93598 __pyx_f_4kivy_10properties_8Property_dispatch + 900
	39  ccc-writer-3                        0x0000000102b91c78 __pyx_f_4kivy_10properties_8Property_set + 1264
	40  ccc-writer-3                        0x0000000102bb6c94 __pyx_tp_descr_set_4kivy_10properties_Property + 864
	41  ccc-writer-3                        0x000000010255d568 _PyObject_GenericSetAttrWithDict + 184
	42  ccc-writer-3                        0x000000010255c8dc PyObject_SetAttr + 132
	43  ccc-writer-3                        0x0000000102bdd6a0 __pyx_tp_setattro_4kivy_9weakproxy_WeakProxy + 224
	44  ccc-writer-3                        0x000000010255c8dc PyObject_SetAttr + 132
	45  ccc-writer-3                        0x00000001025dcd94 _PyEval_EvalFrameDefault + 5336
	46  ccc-writer-3                        0x0000000102519fe0 function_code_fastcall + 120
	47  ccc-writer-3                        0x00000001025e0e6c call_function + 288
	48  ccc-writer-3                        0x00000001025dcdf4 _PyEval_EvalFrameDefault + 5432
	49  ccc-writer-3                        0x00000001025e1e98 _PyEval_EvalCodeWithName + 3212
	50  ccc-writer-3                        0x000000010251a160 _PyFunction_Vectorcall + 248
	51  ccc-writer-3                        0x000000010251c58c method_vectorcall + 156
	52  ccc-writer-3                        0x00000001025e0e6c call_function + 288
	53  ccc-writer-3                        0x00000001025ddd50 _PyEval_EvalFrameDefault + 9364
	54  ccc-writer-3                        0x00000001025e1e98 _PyEval_EvalCodeWithName + 3212
	55  ccc-writer-3                        0x00000001025db87c PyEval_EvalCodeEx + 72
	56  ccc-writer-3                        0x0000000102825f50 __Pyx_PyFunction_FastCallDict + 256
	57  ccc-writer-3                        0x000000010282a5dc __pyx_f_4kivy_6_event_14EventObservers__dispatch + 3020
	58  ccc-writer-3                        0x000000010282b020 __pyx_f_4kivy_6_event_14EventObservers_dispatch + 624
	59  ccc-writer-3                        0x0000000102b93598 __pyx_f_4kivy_10properties_8Property_dispatch + 900
	60  ccc-writer-3                        0x0000000102b91c78 __pyx_f_4kivy_10properties_8Property_set + 1264
	61  ccc-writer-3                        0x0000000102b97dc8 __pyx_f_4kivy_10properties_12DictProperty_set + 612
	62  ccc-writer-3                        0x0000000102bb6c94 __pyx_tp_descr_set_4kivy_10properties_Property + 864
	63  ccc-writer-3                        0x000000010255d568 _PyObject_GenericSetAttrWithDict + 184
	64  ccc-writer-3                        0x000000010255c8dc PyObject_SetAttr + 132
	65  ccc-writer-3                        0x00000001025dcd94 _PyEval_EvalFrameDefault + 5336
	66  ccc-writer-3                        0x0000000102519fe0 function_code_fastcall + 120
	67  ccc-writer-3                        0x000000010251995c PyVectorcall_Call + 104
	68  ccc-writer-3                        0x00000001025de888 _PyEval_EvalFrameDefault + 12236
	69  ccc-writer-3                        0x0000000102519fe0 function_code_fastcall + 120
	70  ccc-writer-3                        0x00000001025e0e6c call_function + 288

	71  ccc-writer-3                        0x00000001025dcdf4 _PyEval_EvalFrameDefault + 5432
	72  ccc-writer-3                        0x0000000102519fe0 function_code_fastcall + 120
	73  ccc-writer-3                        0x00000001025e0e6c call_function + 288
	74  ccc-writer-3                        0x00000001025dcdf4 _PyEval_EvalFrameDefault + 5432
	75  ccc-writer-3                        0x0000000102519fe0 function_code_fastcall + 120
	76  ccc-writer-3                        0x000000010251c5f8 method_vectorcall + 264
	77  ccc-writer-3                        0x000000010251995c PyVectorcall_Call + 104
	78  ccc-writer-3                        0x000000010273cc40 t_bootstrap + 80
	79  ccc-writer-3                        0x000000010262a8e8 pythread_wrapper + 28
	80  libsystem_pthread.dylib             0x00000001cfbb3cb0 _pthread_start + 320
	81  libsystem_pthread.dylib             0x00000001cfbbc778 thread_start + 8
)
2021-04-13 12:23:16.745354+0100 ccc-writer-3[5609:5183173] *** Assertion failure in -[_UISimpleFenceProvider trackSystemAnimationFence:], _UISimpleFenceProvider.m:51
2021-04-13 12:23:16.747549+0100 ccc-writer-3[5609:5183173] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'main thread only'
*** First throw call stack:
(0x184dc686c 0x199de1c50 0x184ccc000 0x18606091c 0x186cd20bc 0x187777d30 0x1877cb888 0x186c00e58 0x1875b2610 0x1871c71b8 0x1871c54d0 0x1871c51f0 0x1871c674c 0x1871c67c8 0x1871c405c 0x1871c3b38 0x1871c2b88 0x1877b7f58 0x1877b2fc8 0x187cb2b30 0x187aacd50 0x1027ee960 0x1027ef164 0x1027b8490 0x10289f0b8 0x10252280c 0x1025e0e6c 0x1025dcdf4 0x1025e1e98 0x10251a160 0x10251c58c 0x1025e0e6c 0x1025ddd50 0x102519fe0 0x1025e0e6c 0x1025dcdf4 0x102519fe0 0x1025e0e6c 0x1025dcdf4 0x1025e1e98 0x10251a160 0x10251c664 0x10251995c 0x1025de888 0x1025e1e98 0x1025db87c 0x102825f50 0x10282a5dc 0x10282b020 0x102b93598 0x102b91c78 0x102bb6c94 0x10255d568 0x10255c8dc 0x102bdd6a0 0x10255c8dc 0x1025dcd94 0x102519fe0 0x1025e0e6c 0x1025dcdf4 0x1025e1e98 0x10251a160 0x10251c58c 0x1025e0e6c 0x1025ddd50 0x1025e1e98 0x1025db87c 0x102825f50 0x10282a5dc 0x10282b020 0x102b93598 0x102b91c78 0x102b97dc8 0x102bb6c94 0x10255d568 0x10255c8dc 0x1025dcd94 0x102519fe0 0x10251995c 0x1025de888 0x102519fe0 0x1025e0e6c 0x1025dcdf4 0x102519fe0 0x1025e0e6c 0x1025dcdf4 0x102519fe0 0x10251c5f8 0x10251995c 0x10273cc40 0x10262a8e8 0x1cfbb3cb0 0x1cfbbc778)
libc++abi.dylib: terminating with uncaught exception of type NSException
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'main thread only'
terminating with uncaught exception of type NSException
(lldb) 

Additional context
Quite a few Swift developers on StackOverflow have posted about this issue. But none so far have been Kivy related.

@Baekalfen
Copy link

I'm having the same issue. Have you found a solution?

@lorddevereux
Copy link

lorddevereux commented Jul 4, 2021

I haven't experienced this specific problem, but I have worked around threading issues by having the main thread callback a simple function using the scheduler e.g.

def callback(self, *largs):
          # called by random child thread 
          Clock.schedule_once(actual_callback)

def actual_callback(self, *largs):
          # called by main thread next tick
          do_stuff()

@gunaNeelamegam
Copy link

I'm having the same issue. Have you found a solution?

I have a issue with ios application based on kivy, can you help for me .

@Julian-O
Copy link
Contributor

Looks like you are modifying a thread-unsafe root.data from a non-background thread.

The advice from StackOverflow is to only modify the UI from the main thread.
The advice above describes how to do it in Kivy.

Closing as Won't Fix: Working As Designed.

@Julian-O Julian-O closed this as not planned Won't fix, can't repro, duplicate, stale Oct 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants