Skip to content
This repository was archived by the owner on Aug 31, 2021. It is now read-only.

Commit 4c4fb21

Browse files
committed
[[ Bug 20124 ]] Add thread affinity for iOS
This patch generalizes the idea of 'thread affinity' introduced to Java FFI for Android, to cover Obj-C and C foreign handlers as well. Java specific types and functions have been renamed to be more general MCScriptThreadAffinity enum based - and the Java code refactored to use it. Two C defines CROSS_COMPILE_TARGET and CROSS_COMPILE_HOST have been added, which one depends on whether the target is being compiled in host or target mode. This is required as GYP generates Xcode projects which build HOST using TARGET defines (e.g. lc-compile is compiled for Mac as if it were targetting iOS). There are now External variants of the HandlerInvoke APIs which will ensure that the HandlerRef is called on the engine thread, and the Obj-C and Java interop code which may call handlers from the ui thread now use them. Similarly, thread jumping is now done either at the point of handler invocation in LCB, or at the point of external HandlerRef invocation from outside of LCB. Essentially, LCB always presumes it is running code on the engine thread (like LCS does). By way of a test, a native iOS button has been added.
1 parent 37d7609 commit 4c4fb21

File tree

18 files changed

+760
-276
lines changed

18 files changed

+760
-276
lines changed

Installer/package.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,8 @@ component Extensions
392392
rfolder macosx:packaged_extensions/com.livecode.widget.native.android.button
393393
into [[ToolsFolder]]/Extensions place
394394
rfolder macosx:packaged_extensions/com.livecode.widget.native.android.field
395+
into [[ToolsFolder]]/Extensions place
396+
rfolder macosx:packaged_extensions/com.livecode.widget.native.ios.button
395397
into [[ToolsFolder]]/Extensions place
396398
rfolder macosx:packaged_extensions/com.livecode.widget.browser
397399
into [[ToolsFolder]]/Extensions place

config/crosscompile.gypi

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,11 @@
8181
{
8282
'toolset_os': '<(host_os)',
8383
'toolset_arch': '<(host_arch)',
84+
8485
},
8586
{
8687
'toolset_os': '<(OS)',
87-
'toolset_arch': '<(target_arch)',
88+
'toolset_arch': '<(target_arch)',
8889
},
8990
],
9091
],
@@ -99,6 +100,24 @@
99100

100101
'target_conditions':
101102
[
103+
[
104+
'_toolset == "host"',
105+
{
106+
'defines':
107+
[
108+
'CROSS_COMPILE_HOST',
109+
],
110+
},
111+
],
112+
[
113+
'_toolset == "target"',
114+
{
115+
'defines':
116+
[
117+
'CROSS_COMPILE_TARGET',
118+
],
119+
},
120+
],
102121
[
103122
'host_and_target != 0',
104123
{

docs/guides/LiveCode Builder Language Reference.md

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -444,10 +444,10 @@ Note: No bridging of types will occur when passing a parameter in the non-fixed
444444
section of a variadic argument list. You must ensure the arguments you pass there
445445
are of the appropriate foreign type (e.g. CInt, CDouble).
446446

447-
There are a number of types defined in the foreign module which map to
448-
the appropriate foreign type when used in foreign handler signatures.
447+
There are a number of types defined in the foreign, java and objc modules which
448+
map to the appropriate foreign type when used in foreign handler signatures.
449449

450-
There are the standard machine types:
450+
There are the standard machine types (defined in the foreign module):
451451

452452
- Bool maps to an 8-bit boolean
453453
- Int8/SInt8 and UInt8 map to 8-bit integers
@@ -457,7 +457,7 @@ There are the standard machine types:
457457
- IntSize/SIntSize and UIntSize map to the integer size needed to hold a memory size
458458
- IntPtr/SIntPtr and UIntPtr map to the integer size needed to hold a pointer
459459

460-
There are the standard C primitive types:
460+
There are the standard C primitive types (defined in the foreign module)
461461

462462
- CBool maps to 'bool'
463463
- CChar, CSChar and CUChar map to 'char', 'signed char' and 'unsigned char'
@@ -468,7 +468,21 @@ There are the standard C primitive types:
468468
- CFloat maps to 'float'
469469
- CDouble maps to 'double'
470470

471-
There are aliases for the Java primitive types:
471+
There are types specific to Obj-C types (defined in the objc module):
472+
473+
- ObjcObject wraps an obj-c 'id', i.e. a pointer to an objective-c object
474+
- ObjcId maps to 'id'
475+
- ObjcRetainedId maps to 'id', and should be used where a foreign handler
476+
argument expects a +1 reference count, or where a foreign handler returns
477+
an id with a +1 reference count.
478+
479+
Note: When an ObjcId is converted to ObjcObject, the id is retained;
480+
when an ObjcObject converted to an ObjcId, the id is not retained. Conversely,
481+
when an ObjcRetainedId is converted to an ObjcObject, the object takes the
482+
+1 reference count (so does not retain); when an ObjcObject is put into an
483+
ObjcRetainedId, a +1 reference count is taken (so does retain).
484+
485+
There are aliases for the Java primitive types (defined in the java module)
472486

473487
- JBoolean maps to Bool
474488
- JByte maps to Int8
@@ -515,8 +529,8 @@ If an out parameter is of a Ref type, then it must be a copy (on exit)
515529
If an inout parameter is of a Ref type, then its existing value must be
516530
released, and replaced by a copy (on exit).
517531

518-
The binding string for foreign handlers is prefixed by a language,
519-
currently either C or Java.
532+
The binding string for foreign handlers is language-specific and currently
533+
supported forms are explained in the following sections.
520534

521535
Foreign handlers' bound symbols are resolved on first use and an error
522536
is thrown if the symbol cannot be found.
@@ -525,18 +539,11 @@ Foreign handlers are always considered unsafe, and thus may only be called
525539
from unsafe context - i.e. from within an unsafe handler, or unsafe statement
526540
block.
527541

528-
> **Note:** The current foreign handler definition is an initial
529-
> version, mainly existing to allow binding to implementation of the
530-
> syntax present in the standard language modules. It will be expanded
531-
> and improved in a subsequent version to make it very easy to import
532-
> and use functions (and types) from other languages including
533-
> Objective-C (on Mac and iOS) and Java (on Android).
534-
535542
#### The C binding string
536543

537544
The C binding string has the following form:
538545

539-
"c:[library>][class.]function[!calling]"
546+
"c:[library>][class.]function[!calling][?thread]"
540547

541548
Here *library* specifies the name of the library to bind to (if no
542549
library is specified a symbol from the engine executable is assumed).
@@ -561,6 +568,26 @@ All but `default` are Win32-only, and on Win32 `default` maps to
561568
`cdecl`. If a Win32-only calling convention is specified on a
562569
non-Windows platform then it is taken to be `default`.
563570

571+
Here *thread* is either empty or `ui`. The `ui` form is used to determine
572+
whether the method should be run on the UI thread (currently only applicable
573+
on Android and iOS).
574+
575+
#### The Obj-C binding string
576+
577+
The Obj-C binding string has the following form:
578+
579+
"objc:class.(+|-)method[?thread]"
580+
581+
Here *class* specifies the name of the class containing the method to bind to.
582+
583+
Here *method* specifies the method name to bind to in standard Obj-C selector
584+
form, e.g. addTarget:action:forControlEvents:. If the method is a class method
585+
then prefix it with '+', if it is an instance method then prefix it with '-'.
586+
587+
Here *thread* is either empty or `ui`. The `ui` form is used to determine
588+
whether the method should be run on the UI thread (currently only applicable
589+
on Android and iOS).
590+
564591
#### The Java binding string
565592

566593
The Java binding string has the following form:
@@ -665,8 +692,9 @@ Instance and nonvirtual calling conventions require instances of the given
665692
Java class, so the foreign handler declaration will always require a Java
666693
object parameter.
667694

668-
Here, *thread* is either empty or `ui`. The `ui` form is used on
669-
Android when binding to methods that must be run on the UI thread.
695+
Here, *thread* is either empty or `ui`. The `ui` form is used to determine
696+
whether the method should be run on the UI thread (currently only applicable
697+
on Android and iOS).
670698

671699
> **Warning:** At the moment it is not advised to use callbacks that may be
672700
> executed on arbitrary threads, as this is likely to cause your application

docs/lcb/notes/20124.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# LiveCode Builder Language
2+
3+
## Foreign Function Interface
4+
5+
* It is now possible to specify the thread to be used in Obj-C foreign handlers. This is done by appending `?<thread>` to the end of the binding string. Currently the only supported value is `ui`, for running the handler on the iOS main thread, as opposed to the engine thread.

engine/src/mblandroidlcb.cpp

Lines changed: 4 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -35,52 +35,13 @@
3535

3636
extern jobject MCJavaPrivateDoNativeListenerCallback(jlong p_handler, jstring p_method_name, jobjectArray p_args);
3737

38-
#ifdef TARGET_SUBPLATFORM_ANDROID
39-
struct remote_call_t
40-
{
41-
jlong p_handler;
42-
jstring p_method_name;
43-
jobjectArray p_args;
44-
jobject *t_return;
45-
};
46-
47-
static void remote_call_func(void *p_context)
48-
{
49-
auto ctxt = static_cast<remote_call_t *>(p_context);
50-
*(ctxt->t_return) = MCJavaPrivateDoNativeListenerCallback(ctxt->p_handler,
51-
ctxt->p_method_name,
52-
ctxt->p_args);
53-
}
54-
#endif
55-
5638
extern "C" JNIEXPORT jobject JNICALL Java_com_runrev_android_LCBInvocationHandler_doNativeListenerCallback(JNIEnv *env, jobject object, jlong handler, jstring p_method, jobjectArray p_args) __attribute__((visibility("default")));
5739

5840
JNIEXPORT jobject JNICALL Java_com_runrev_android_LCBInvocationHandler_doNativeListenerCallback(JNIEnv *env, jobject object, jlong p_handler, jstring p_method_name, jobjectArray p_args)
5941
{
60-
jobject t_global_return = nullptr;
61-
#ifdef TARGET_SUBPLATFORM_ANDROID
62-
extern bool MCAndroidIsOnEngineThread(void);
63-
if (!MCAndroidIsOnEngineThread())
64-
{
65-
// Take globalrefs of java objects we need to persist after
66-
// jumping threads
67-
p_args = static_cast<jobjectArray>(env->NewGlobalRef(p_args));
68-
p_method_name = static_cast<jstring>(env->NewGlobalRef(p_method_name));
69-
typedef void (*co_yield_callback_t)(void *);
70-
extern void co_yield_to_engine_and_call(co_yield_callback_t callback, void *context);
71-
remote_call_t t_context = {p_handler, p_method_name, p_args, &t_global_return};
72-
co_yield_to_engine_and_call(remote_call_func, &t_context);
73-
env->DeleteGlobalRef(p_args);
74-
env->DeleteGlobalRef(p_method_name);
75-
}
76-
else
77-
{
78-
#endif
79-
t_global_return = MCJavaPrivateDoNativeListenerCallback(p_handler, p_method_name, p_args);
80-
#ifdef TARGET_SUBPLATFORM_ANDROID
81-
}
82-
#endif
83-
42+
jobject t_result =
43+
MCJavaPrivateDoNativeListenerCallback(p_handler, p_method_name, p_args);
44+
8445
// At the moment we have no way of dealing with any errors thrown in
8546
// the course of handling or attempting to handle the native listener
8647
// callback, so
@@ -93,17 +54,7 @@ JNIEXPORT jobject JNICALL Java_com_runrev_android_LCBInvocationHandler_doNativeL
9354
MCsystem->Debug(*t_string);
9455
}
9556

96-
// Turn global ref'd return into a local ref for this env, so it
97-
// can be garbage-collected
98-
99-
jobject t_return = nullptr;
100-
if (t_global_return != nullptr)
101-
{
102-
t_return = env->NewLocalRef(t_global_return);
103-
env->DeleteGlobalRef(t_global_return);
104-
}
105-
106-
return t_return;
57+
return t_result;
10758
}
10859

10960
////////////////////////////////////////////////////////////////////////////////

engine/src/mbliphonedc.mm

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,11 @@ void MCIPhoneCallOnMainFiber(void (*handler)(void *), void *context)
214214
MCFiberCall(s_main_fiber, handler, context);
215215
}
216216

217+
bool MCIPhoneIsOnMainFiber(void)
218+
{
219+
return MCFiberIsCurrentThread(s_main_fiber);
220+
}
221+
217222
////////////////////////////////////////////////////////////////////////////////
218223

219224
Boolean MCScreenDC::open(void)

extensions/extensions.gyp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
'widgets/androidbutton/androidbutton.lcb',
3030
'widgets/androidfield/androidfield.lcb',
3131
'widgets/macbutton/macbutton.lcb',
32+
'widgets/iosbutton/iosbutton.lcb',
3233
'widgets/browser/browser.lcb',
3334
#’widgets/chart/chart.lcb',
3435
#'widgets/checkbox/checkbox.lcb',

0 commit comments

Comments
 (0)