xamarin-macios/runtime/shared.m

206 строки
6.2 KiB
Mathematica
Исходник Обычный вид История

2016-04-21 15:19:32 +03:00
//
// shared.m: Shared native code between Xamarin.iOS and Xamarin.Mac.
//
//
// Authors:
// Rolf Bjarne Kvinge <rolf@xamarin.com>
//
// Copyright 2013 Xamarin Inc.
//
#include <objc/objc.h>
#include <objc/runtime.h>
#include <objc/message.h>
#import <Foundation/Foundation.h>
#include "xamarin/xamarin.h"
#include "shared.h"
#include "runtime-internal.h"
2016-04-21 15:19:32 +03:00
/*
* XamarinNSThreadObject and xamarin_init_nsthread is a fix for a problem that
* occurs because NSThread will retain the 'object' argument to the
* initWithTarget:selector:object: selector, and release it using a tls dtor.
* The issue is that calling release on that object may end up in managed code,
* but calling the mono runtime from a tls dtor is not allowed.
*
* So we create a native wrapper object which is the object that ends up released
* on the tls dtor, and then it forwards the release of the managed object to
* the main thread.
*
* Bug: https://bugzilla.xamarin.com/show_bug.cgi?id=13612
*/
@interface XamarinNSThreadObject : NSObject
{
void *target;
SEL selector;
id argument;
bool is_direct_binding;
}
-(id) initWithData: (void *) targ selector:(SEL) sel argument:(id) arg is_direct_binding:(bool) is_direct;
-(void) start: (id) arg;
-(void) dealloc;
@end
@implementation XamarinNSThreadObject
{
}
-(id) initWithData: (void *) targ selector:(SEL) sel argument:(id) arg is_direct_binding:(bool) is_direct;
{
// COOP: no managed memory access: any mode.
2016-04-21 15:19:32 +03:00
target = targ;
if (is_direct)
[((id) targ) retain];
selector = sel;
argument = [arg retain];
is_direct_binding = is_direct;
return self;
}
-(void) start: (id) arg;
{
// COOP: no managed memory access: any mode.
2016-04-21 15:19:32 +03:00
if (is_direct_binding) {
id (*invoke) (id, SEL, id) = (id (*)(id, SEL, id)) objc_msgSend;
invoke ((id) target, selector, argument);
} else {
id (*invoke) (struct objc_super *, SEL, id) = (id (*)(struct objc_super *, SEL, id)) objc_msgSendSuper;
invoke ((struct objc_super *) target, selector, argument);
}
}
-(void) dealloc;
{
// COOP: no managed memory access: any mode.
2016-04-21 15:19:32 +03:00
// Cast to void* so that ObjC doesn't do any funky retain/release
// on these objects when capturing them for the block, since we
// may be on a thread where we cannot end up in the mono runtime,
// and retain/release may do exactly that.
void *targ = (void *) (is_direct_binding ? target : nil);
void *arg = (void *) argument;
dispatch_async (dispatch_get_main_queue (), ^{
[((id) targ) release];
[((id) arg) release];
});
[super dealloc];
}
@end
id
xamarin_init_nsthread (id obj, bool is_direct, id target, SEL sel, id arg)
{
// COOP: no managed memory access: any mode.
2016-04-21 15:19:32 +03:00
XamarinNSThreadObject *wrap = [[[XamarinNSThreadObject alloc] initWithData: target selector: sel argument: arg is_direct_binding: is_direct] autorelease];
id (*invoke) (id, SEL, id, SEL, id) = (id (*)(id, SEL, id, SEL, id)) objc_msgSend;
return invoke (obj, @selector(initWithTarget:selector:object:), wrap, @selector(start:), nil);
}
/* Protecting the Cocoa Frameworks
*
* To let Cocoa know that you intend to use multiple threads, all you have
* to do is spawn a single thread using the NSThread class and let that
* thread immediately exit. Your thread entry point need not do anything.
* Just the act of spawning a thread using NSThread is enough to ensure that
* the locks needed by the Cocoa frameworks are put in place.
*
* Source: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html#//apple_ref/doc/uid/20000738-125024
*
* Ref: https://bugzilla.xamarin.com/show_bug.cgi?id=798
*/
@interface XamarinCocoaThreadInitializer : NSObject
2016-04-21 15:19:32 +03:00
{
init_cocoa_func *the_func;
}
-(void) entryPoint: (NSObject *) obj;
-(id) initWithFunc: (init_cocoa_func *) func;
@end
@implementation XamarinCocoaThreadInitializer
2016-04-21 15:19:32 +03:00
{
}
-(void) entryPoint: (NSObject *) obj
{
// COOP: no managed memory access: any mode.
2016-04-21 15:19:32 +03:00
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if (the_func != NULL)
the_func ();
[pool drain];
}
-(id) initWithFunc: (init_cocoa_func *) func;
{
// COOP: no managed memory access: any mode.
2016-04-21 15:19:32 +03:00
self = [super init];
if (self != nil) {
the_func = func;
[NSThread detachNewThreadSelector:@selector(entryPoint:) toTarget:self withObject:nil];
}
return self;
}
@end
void
xamarin_initialize_cocoa_threads (init_cocoa_func *func)
2016-04-21 15:19:32 +03:00
{
// COOP: no managed memory access: any mode.
[[[XamarinCocoaThreadInitializer alloc] initWithFunc: func] autorelease];
2016-04-21 15:19:32 +03:00
}
/* Threads & Blocks
*
* At the moment we can't execute managed code in the dispose method for a block (the process may deadlock,
* depending on the thread the dispose method is called on).
*
* ref: https://bugzilla.xamarin.com/show_bug.cgi?id=11286
* ref: https://bugzilla.xamarin.com/show_bug.cgi?id=14954
*
*/
static void
xamarin_dispose_helper (void *a)
{
// COOP: this method is executed by the ObjC runtime when a block must be freed.
// COOP: it does not touch any managed memory (except to free a gchandle), so any mode goes.
2016-04-21 15:19:32 +03:00
struct Block_literal *bl = (struct Block_literal *) a;
xamarin_gchandle_free (bl->global_handle);
bl->global_handle = INVALID_GCHANDLE;
[runtime] Use newer atomic functions. (#7119) Fixes these warnings: shared.m:252:6: warning: 'OSAtomicDecrement32Barrier' is deprecated: first deprecated in watchOS 3.0 - Use std::atomic_fetch_sub() from <atomic> instead [-Wdeprecated-declarations] if (OSAtomicDecrement32Barrier (&bl->descriptor->ref_count) == 0) { ^ /Applications/Xcode11.app/Contents/Developer/Platforms/WatchOS.platform/Developer/SDKs/WatchOS6.0.sdk/usr/include/libkern/OSAtomicDeprecated.h:201:9: note: 'OSAtomicDecrement32Barrier' has been explicitly marked deprecated here int32_t OSAtomicDecrement32Barrier( volatile int32_t *__theValue ); ^ shared.m:270:2: warning: 'OSAtomicIncrement32' is deprecated: first deprecated in watchOS 3.0 - Use std::atomic_fetch_add_explicit(std::memory_order_relaxed) from <atomic> instead [-Wdeprecated-declarations] OSAtomicIncrement32 (&source->descriptor->ref_count); ^ /Applications/Xcode11.app/Contents/Developer/Platforms/WatchOS.platform/Developer/SDKs/WatchOS6.0.sdk/usr/include/libkern/OSAtomicDeprecated.h:171:9: note: 'OSAtomicIncrement32' has been explicitly marked deprecated here int32_t OSAtomicIncrement32( volatile int32_t *__theValue ); ^ monotouch-debug.m:309:10: warning: 'OSAtomicIncrement32Barrier' is deprecated: first deprecated in watchOS 3.0 - Use std::atomic_fetch_add() from <atomic> instead [-Wdeprecated-declarations] int c = OSAtomicIncrement32Barrier (&http_send_counter); ^ /Applications/Xcode11.app/Contents/Developer/Platforms/WatchOS.platform/Developer/SDKs/WatchOS6.0.sdk/usr/include/libkern/OSAtomicDeprecated.h:182:9: note: 'OSAtomicIncrement32Barrier' has been explicitly marked deprecated here int32_t OSAtomicIncrement32Barrier( volatile int32_t *__theValue ); ^
2019-09-27 08:15:59 +03:00
if (atomic_fetch_sub (&bl->descriptor->ref_count, 1) == 0) {
Share the same block descriptors between copies of a block. Fixes #43592. (#683) In bug #43592 the following occurs: * App calls an API that takes a block. * We create a stack-based ObjC block based on the delegate the app provided. This block has a pointer to a block description, describing the block in question (including the signature of the block, as an ObjC-type encoded string). We allocate a new block description for every block. * Apple's API stores the pointer to the signature string somewhere. * Apple calls _Block_copy to get a heap-based block. * We create a heap-based block, and copy the entire description into the new heap-based block (including a copy of the signature). * Apple returns from the API, and then we free the stack-based block (and the descriptor, and thus the signature string in the descriptor). * Apple uses the pointer to the signature stored previously to investigate the signature of the block, and crashes because this signature has been freed. The assumption in Apple's code is that the description will never be freed, which is true for any Xcode project (clang will always be able to create the block description at compile-time and emit it in the binary, which means the memory will never be freed). We could potentially do the same thing in the static registrar, but we'd still need a solution when using the dynamic registrar. To fix this instead of copying the entire description structure when creating a heap-based block from the stack-based block, we make the description ref- counted, and just use the same description in the heap-based block. The signature will now stay in memory until both the heap-based and stack- based blocks have been freed, and we hope Apple doesn't have any API that needs the signature after all the blocks for that signature have been freed. https://bugzilla.xamarin.com/show_bug.cgi?id=43592
2016-08-26 20:22:38 +03:00
free (bl->descriptor); // allocated using Marshal.AllocHGlobal.
}
2016-04-21 15:19:32 +03:00
bl->descriptor = NULL;
}
static void
xamarin_copy_helper (void *dst, void *src)
{
// COOP: this method is executed by the ObjC runtime when a block must be copied.
// COOP: it does not touch any managed memory (except to allocate a gchandle), so any mode goes.
2016-04-21 15:19:32 +03:00
struct Block_literal *source = (struct Block_literal *) src;
struct Block_literal *target = (struct Block_literal *) dst;
MonoObject *mobj = xamarin_gchandle_get_target (source->local_handle);
target->global_handle = xamarin_gchandle_new (mobj, FALSE);
xamarin_mono_object_release (&mobj);
2016-04-21 15:19:32 +03:00
[runtime] Use newer atomic functions. (#7119) Fixes these warnings: shared.m:252:6: warning: 'OSAtomicDecrement32Barrier' is deprecated: first deprecated in watchOS 3.0 - Use std::atomic_fetch_sub() from <atomic> instead [-Wdeprecated-declarations] if (OSAtomicDecrement32Barrier (&bl->descriptor->ref_count) == 0) { ^ /Applications/Xcode11.app/Contents/Developer/Platforms/WatchOS.platform/Developer/SDKs/WatchOS6.0.sdk/usr/include/libkern/OSAtomicDeprecated.h:201:9: note: 'OSAtomicDecrement32Barrier' has been explicitly marked deprecated here int32_t OSAtomicDecrement32Barrier( volatile int32_t *__theValue ); ^ shared.m:270:2: warning: 'OSAtomicIncrement32' is deprecated: first deprecated in watchOS 3.0 - Use std::atomic_fetch_add_explicit(std::memory_order_relaxed) from <atomic> instead [-Wdeprecated-declarations] OSAtomicIncrement32 (&source->descriptor->ref_count); ^ /Applications/Xcode11.app/Contents/Developer/Platforms/WatchOS.platform/Developer/SDKs/WatchOS6.0.sdk/usr/include/libkern/OSAtomicDeprecated.h:171:9: note: 'OSAtomicIncrement32' has been explicitly marked deprecated here int32_t OSAtomicIncrement32( volatile int32_t *__theValue ); ^ monotouch-debug.m:309:10: warning: 'OSAtomicIncrement32Barrier' is deprecated: first deprecated in watchOS 3.0 - Use std::atomic_fetch_add() from <atomic> instead [-Wdeprecated-declarations] int c = OSAtomicIncrement32Barrier (&http_send_counter); ^ /Applications/Xcode11.app/Contents/Developer/Platforms/WatchOS.platform/Developer/SDKs/WatchOS6.0.sdk/usr/include/libkern/OSAtomicDeprecated.h:182:9: note: 'OSAtomicIncrement32Barrier' has been explicitly marked deprecated here int32_t OSAtomicIncrement32Barrier( volatile int32_t *__theValue ); ^
2019-09-27 08:15:59 +03:00
atomic_fetch_add (&source->descriptor->ref_count, 1);
Share the same block descriptors between copies of a block. Fixes #43592. (#683) In bug #43592 the following occurs: * App calls an API that takes a block. * We create a stack-based ObjC block based on the delegate the app provided. This block has a pointer to a block description, describing the block in question (including the signature of the block, as an ObjC-type encoded string). We allocate a new block description for every block. * Apple's API stores the pointer to the signature string somewhere. * Apple calls _Block_copy to get a heap-based block. * We create a heap-based block, and copy the entire description into the new heap-based block (including a copy of the signature). * Apple returns from the API, and then we free the stack-based block (and the descriptor, and thus the signature string in the descriptor). * Apple uses the pointer to the signature stored previously to investigate the signature of the block, and crashes because this signature has been freed. The assumption in Apple's code is that the description will never be freed, which is true for any Xcode project (clang will always be able to create the block description at compile-time and emit it in the binary, which means the memory will never be freed). We could potentially do the same thing in the static registrar, but we'd still need a solution when using the dynamic registrar. To fix this instead of copying the entire description structure when creating a heap-based block from the stack-based block, we make the description ref- counted, and just use the same description in the heap-based block. The signature will now stay in memory until both the heap-based and stack- based blocks have been freed, and we hope Apple doesn't have any API that needs the signature after all the blocks for that signature have been freed. https://bugzilla.xamarin.com/show_bug.cgi?id=43592
2016-08-26 20:22:38 +03:00
target->descriptor = source->descriptor;
2016-04-21 15:19:32 +03:00
}
struct Xamarin_block_descriptor xamarin_block_descriptor =
{
0,
sizeof (struct Block_literal),
xamarin_copy_helper,
xamarin_dispose_helper,
NULL,
0,
};
struct Xamarin_block_descriptor *
xamarin_get_block_descriptor ()
{
// COOP: no managed memory access: any mode.
2016-04-21 15:19:32 +03:00
return &xamarin_block_descriptor;
}