Callback when a block is deallocated - objective-c

I am calling into Cocoa from C, all through the Obj-C runtime.
I am able to create block objects with the info from here[1] and pass them as arguments to Cocoa methods which retain them as needed, and release them when they are no longer needed. The problem is that I need to release other resources associated with the block when the block reaches refcount 0 and is deallocated, so I need a way to set a callback for when that happens.
With normal objects, I would just subclass and override dealloc(). I hear blocks are objects too - is there a Block class that can be subclassed? Or is there any other way to hook up a function on release and/or dealloc of blocks?
Thanks.
[1] http://clang.llvm.org/docs/Block-ABI-Apple.html

You can use the Obj-C Associated Objects API to associate an object instance with a block instance. The associated object will (if it is not accessed anywhere else) be deallocated when the block is deallocated.
Use the -dealloc method of the associated object to execute any desired resource cleanup, etc.

Expanding on my comment:
I'll assume you are using the Clang compiler to create your blocks in C, if you are creating the block description structs yourself the idea is the same but you can create the structs directly with the correct values.
If you wish to call a cleanup function when a block is disposed of then (in outline):
if (bObject->flags & BLOCK_HAS_COPY_DISPOSE)
{
// block already has a dispose helper
// save current dispose helper in a lookup table with key the bObject
bObject->descriptor->dispose_helper = function which:
a) uses the lookup table to call the original helper
b) removes the entry from the lookup table
c) calls your cleanup function
}
else
{
// block does not have a dispose helper
bObject->flags |= BLOCK_HAS_COPY_DISPOSE; // set is has helpers
bObject->descriptor->copy_helper = dummy copy function
bObject->descriptor->dispose_helper = dispose function which just calls your cleanup
}
You need a lookup table to store a map from block addresses to helper addresses, e.g. NSMapTable.
HTH
Addendum
As requested in comments my quick'n'dirty test code, it just follows the pseudo-code above. Run this and you should see the second and third blocks get disposed, the first is not as its a static literal and doesn't need disposing.
void DummyBlockCopy(void *src, void *dst)
{
}
void BlockDispose(void *src)
{
printf("BlockDispose %p\n", src);
}
typedef void (*HelperFunction)(void *);
NSMapTable *disposeHelpers;
void BlockDisposeCallExisting(void *src)
{
HelperFunction helper = (__bridge void *)[disposeHelpers objectForKey:(__bridge id)(src)];
if (helper)
{
helper(src);
[disposeHelpers removeObjectForKey:(__bridge id)(src)];
}
printf("BlockDisposeCallExisting %p\n", src);
}
void block_trap_dispose(void *aBlock)
{
BlockObject *bObject = aBlock;
if (bObject->flags & BLOCK_HAS_COPY_DISPOSE)
{
[disposeHelpers setObject:(__bridge id)(void *)bObject->descriptor->dispose_helper forKey:(__bridge id)(aBlock)];
bObject->descriptor->dispose_helper = BlockDisposeCallExisting;
}
else
{
bObject->flags |= BLOCK_HAS_COPY_DISPOSE;
bObject->descriptor->copy_helper = DummyBlockCopy;
bObject->descriptor->dispose_helper = BlockDispose;
}
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
disposeHelpers = [NSMapTable.alloc initWithKeyOptions:(NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality)
valueOptions:(NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality)
capacity:2];
void (^b1)(void) = ^{ printf("hello world\n"); };
printf("b1: %p\n", b1);
b1();
block_trap_dispose((__bridge void *)(b1));
int x = 10;
void (^b2)(void) = ^{ printf("x is %d\n", x); };
printf("b2: %p\n", b2);
b2();
block_trap_dispose((__bridge void *)(b2));
NSObject *anObject = NSObject.new;
void (^b3)(void) = ^{ printf("anObject: %p\n", anObject); };
printf("b3: %p\n", b3);
b3();
block_trap_dispose((__bridge void *)(b3));
}

Ok, this is how I solved it.
First I created a block_literal (defined to have a block_descriptor attached).
struct block_descriptor {
unsigned long int reserved; // NULL
unsigned long int size; // sizeof(struct block_literal)
copy_helper_t copy_helper; // IFF (1<<25)
dispose_helper_t dispose_helper; // IFF (1<<25)
};
struct block_literal {
struct block_literal *isa;
int flags;
int reserved;
void *invoke;
struct block_descriptor *descriptor;
struct block_descriptor d; // because they come in pairs
};
This is how you should set the fields:
block.isa = _NSConcreteStackBlock //stack block because global blocks are not copied/disposed
block.flags = 1<<25 //has copy & dispose helpers
block.reserved = 0
block.invoke = my_callback_function
block.descriptor = &block.d
block.d.reserved = 0
block.d.size = sizeof(block_literal)
block.d.copy_helper = my_copy_callback
block.d.dispose_helper = my_dispose_callback
I keep a refcount per each created block which starts at 1, and which is incremented in my_copy_callback and decremented in my_dispose_callback. When the refcount reaches 0 the resources associated with the block gets released.
Note: copy/dispose helpers will not be called on synchronous methods like NSString's enumerateLinesUsingBlock because these methods don't retain/release the block while using it because they assume that the block remains available for the duration of the call. OTOH, an async method like dispatch_async() does invoke the helpers. Calling dispatch_async() multiple times on the same block should show the refcount incremented twice and then decremented.

Related

C++/CLI pin_ptr's usage in proper way

I am newbie of C++/CLI.
I already know that the pin_ptr's functionality is making GC not to learn to specified object.
now let me show you msdn's example.
https://msdn.microsoft.com/en-us//library/1dz8byfh.aspx
// pin_ptr_1.cpp
// compile with: /clr
using namespace System;
#define SIZE 10
#pragma unmanaged
// native function that initializes an array
void native_function(int* p) {
for(int i = 0 ; i < 10 ; i++)
p[i] = i;
}
#pragma managed
public ref class A {
private:
array<int>^ arr; // CLR integer array
public:
A() {
arr = gcnew array<int>(SIZE);
}
void load() {
pin_ptr<int> p = &arr[0]; // pin pointer to first element in arr
int* np = p; // pointer to the first element in arr
native_function(np); // pass pointer to native function
}
int sum() {
int total = 0;
for (int i = 0 ; i < SIZE ; i++)
total += arr[i];
return total;
}
};
int main() {
A^ a = gcnew A;
a->load(); // initialize managed array using the native function
Console::WriteLine(a->sum());
}
hear is the question.
Isn't it okay, the passed object(arr) not pinned ?
because the unmanaged code(native_function) is sync operation and finished before the C++/CLI code (load)
is there any chance the gc destory arr, even though the main logic is running?
(I think A is main's stack variable and arr is A's member variable, so while running main, it should visible)
if so, how can we guarantee that the A is there before invoking load?
(only while not running in native-code?)
int main() {
A^ a = gcnew A;
// I Think A or arr can be destroyed in here, if it is able to be destroyed in native_function.
a->load();
...
}
Thanks, in advance.
The problem that is solved by pinning a pointer is not a normal concurrency issue. It might be true that no other thread will preempt the execution of your native function. However, you have to count in the garbage collector, which might kick in whenever the .NET runtime sees fit. For instance, the system might run low on memory, so the runtime decides to collect disposed objects. This might happen while your native function executes, and the garbage collector might relocate the array it is using, so the pointer you passed in isn't valid anymore.
The golden rule of thumb is to pin ALL array pointers and ALL string pointers before passing them to a native function. ALWAYS. Don't think about it, just do it as a rule. Your code might work fine for a long time without pinning, but one day bad luck will strike you just when it's most annoying.

Why can setting a Block variable inside an if statement cause a carsh?

I found an block example in the book "Effective Objective-C 2.0"
void (^block)();
if (/* some condition */) {
block = ^ {
NSLog(#"Block A");
};
} else {
block = ^ {
NSLog(#"Block B");
};
}
block();
The code is dangerous, and here is the explanation in the book:
The two blocks that are defined within the if and else statements are allocated within stack memory. When it allocates stack memory for each block, the compiler is free to overwrite this memory at the end of the scope in which that memory was allocated. So each block is guaranteed to be valid only within its respective if-statement section. The code would compile without error but at runtime may or may not function correctly. If it didn’t decide to produce code that overwrote the chosen block, the code would run without error, but if it did, a crash would certainly occur.
I don't understand the meaning of "If it didn’t decide to produce code that overwrote the chosen block, the code would run without error, but if it did, a crash would certainly occur."
Can someone explain and give examples?
The issue is similar to that of a C array being created locally to a function and then used after the function returns:
#import <Foundation/Foundation.h>
dispatch_block_t global_block;
int * global_arr;
void set_globals(void)
{
if( YES ){
global_block = ^{
NSLog(#"Summer is butter on your chin and corn mush between every tooth.");
};
int arr[5] = {1, 2, 3, 4, 5};
global_arr = arr;
}
}
void write_on_the_stack(int i)
{
int arr[5] = {64, 128, 256, 512, 1024};
int v = arr[3];
dispatch_block_t b = ^{
int j = i + 10;
j += v;
};
b();
}
int main(int argc, const char * argv[])
{
#autoreleasepool {
set_globals();
write_on_the_stack();
global_block();
NSLog(#"%d", global_arr[0]); // Prints garbage
}
return 0;
}
The space on the stack that was used to store the values of the array may be re-used for any purpose. I use the separate function here because it most reliably demonstrates the problem. For your exact case, with the if block and the access in the same function, the compiler is still free to re-use the stack space. It may not, but you can't rely on that. You're breaking the scope rules of the language (derived from C).
As Jesse Rusak and CrimsonChris pointed out in comments, though, with a Block-type variable compiled under ARC, the Block is created on the stack like the array, but copied off the stack (to the heap) when it's stored in a strong pointer. All object pointers, including your global, are strong by default.
If you were not compiling with ARC, this would be unreliable. I can't come up with a failing example with my current compiler, but again, it's breaking the rules and the compiler is under no obligation to do what you want.
Essentially what this is saying is that if there's code running on a separate thread, and something gets assigned to the area of memory currently used by block but before the block() call, then bad things will happen.
void (^block)();
if (/* some condition *)) {
block = ^ {
NSLog(#"Block A");
}
} else {
block = ^ {
NSLog(#"Block B");
}
}
<--- another thread overwrites the **block** block
block(); <--- runtime error since **block** has been dereferenced.

Disk Arbitration Objective C: Put All Drives and Partitions in an Array

I am just starting out in this wacky world of programming and I have come across a very frustrating problem:
I am trying to use Disk Arbitration Framework to put all Disks in a array.
#import "DiskDetector.h"
#import "Disk.h"
#implementation DiskDetector
-(id)init {
self.arrayOfDisks = [[NSMutableArray alloc]init];
DASessionRef session;
session = DASessionCreate(kCFAllocatorDefault);
DARegisterDiskAppearedCallback(session, kDADiskDescriptionMatchVolumeMountable,diskAppearedCallback,NULL);
DARegisterDiskDisappearedCallback(session,kDADiskDescriptionMatchVolumeMountable, diskDisappearedCallback, NULL);
DASessionScheduleWithRunLoop(session, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
return self;
}
-(void)addToArrayOfDisks:(Disk*)disk {
}
void diskAppearedCallback(DADiskRef disk, void* context)
{
Disk *theDisk = [[Disk alloc]initWithNewDisk:disk];
NSLog(#"Disk Name: %#",theDisk.diskName);
NSLog(#"Disk Number: %#",theDisk.diskBSDName);
NSLog(#"Disk Connection Type: %#",theDisk.diskConnection);
NSLog(#"Disk Capacity in Bytes: %#",theDisk.diskTotalCapacityInBytes);
NSLog(#"Volume Name: %#",theDisk.partitionScheme);
//How Do I return "theDisk" from this function?
//[self.arrayOfDisks addObject:theDisk] does not work...complier has problem with use of "self"
}
void diskDisappearedCallback(DADiskRef disk, void* context)
{
NSLog(#"%s was ejected", DADiskGetBSDName(disk));
}
#end
As you can see, everything logs okay. The issue is that I want to return "theDisk" object in some way So I can work with it.
Since the callback function is void, I cannot do a return statement. If I attempt to modify the function's return type the DARegisterDiskAppearedCallback Function will not work entirely.
Again, my only goal here is to get information about all disks and partitions on computer and put them in an array so I can get info about them and manipulate them.
Also, can anyone explain when one would put something in "(void*)context" in the callback function? Apple Documentation is very vague on this...or maybe I am missing something entirely
The context parameter is for your use. You can set whatever you want as the context, and DiskArbitration will pass it to you when it calls the callback. You can use this to solve your first problem by passing your DiskDetector object as the context:
-(id)init {
// ...
DARegisterDiskAppearedCallback(session, kDADiskDescriptionMatchVolumeMountable, diskAppearedCallback, self);
// ...
}
void diskAppearedCallback(DADiskRef disk, void* context) {
DiskDetector *detector = (DiskDetector *)context;
// ...
[detector.arrayOfDisks addObject:theDisk];
}

Don't understand example given of block scope

In the Apple block documentation is an example of code not to write:
void dontDoThisEither() {
void (^block) (void);
int i = random();
if (i > 1000) {
block = ^{printf("got i at: %d\n", i); };
}
// ...
}
The comments for the code say the block literal scope is the "then" clause.
I don't understand what they mean by that, there is no then clause, which is presumably why they have put it in quotes. But why have they put it in quotes and what is the relationship to the block's scope?
Think of an if statement as:
if this then that else this other thing
The {... block = ...} is in the then that part of the if statement. That is, it is *a sub-scope of the scope of the dontDoThisEither() function.
Because blocks are created on the stack and are only valid within the scope of their declaration, that means that the block assignment in that example is only valid within the then that scope of the if statement.
I.e. Consider:
void dontDoThisEither() {
void (^block) (void);
int i = random();
if (i > 1000) {
block = ^{printf("got i at: %d\n", i); };
} else {
block = ^{printf("your number is weak and small. ignored.\n");};
}
block();
}
At the time block(); is executed the block that it is pointing to is in a scope that is no longer valid and the behavior will be undefined (and likely crashy in a real world example).

Is there a way to wrap an ObjectiveC block into function pointer?

I have to provide a C-style callback for a specific C library in an iOS app. The callback has no void *userData or something similar. So I am not able to loop in a context. I'd like to avoid introducing a global context to solve this. An ideal solution would be an Objective-C block.
My question: Is there a way to 'cast' a block into a function pointer or to wrap/cloak it somehow?
Technically, you could get access to a function pointer for the block. But it's totally unsafe to do so, so I certainly don't recommend it. To see how, consider the following example:
#import <Foundation/Foundation.h>
struct Block_layout {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor *descriptor;
};
int main(int argc, char *argv[]) {
#autoreleasepool {
// Block that doesn't take or return anything
void(^block)() = ^{
NSLog(#"Howdy %i", argc);
};
// Cast to a struct with the same memory layout
struct Block_layout *blockStr = (struct Block_layout *)(__bridge void *)block;
// Now do same as `block()':
blockStr->invoke(blockStr);
// Block that takes an int and returns an int
int(^returnBlock)(int) = ^int(int a){
return a;
};
// Cast to a struct with the same memory layout
struct Block_layout *blockStr2 = (struct Block_layout *)(__bridge void *)returnBlock;
// Now do same as `returnBlock(argc)':
int ret = ((int(*)(void*, int a, ...))(blockStr2->invoke))(blockStr2, argc);
NSLog(#"ret = %i", ret);
}
}
Running that yields:
Howdy 1
ret = 1
Which is what we'd expect from purely executing those blocks directly with block(). So, you could use invoke as your function pointer.
But as I say, this is totally unsafe. Don't actually use this!
If you want to see a write-up of a way to do what you're asking, then check this out:
http://www.mikeash.com/pyblog/friday-qa-2010-02-12-trampolining-blocks-with-mutable-code.html
It's just a great write-up of what you would need to do to get this to work. Sadly, it's never going to work on iOS though (since you need to mark a page as executable which you're not allowed to do within your app's sandbox). But nevertheless, a great article.
If your block needs context information, and the callback does not offer any context, I'm afraid the answer is a clear no. Blocks have to store context information somewhere, so you will never be able to cast such a block into a no-arguments function pointer.
A carefully designed global variable approach is probably the best solution in this case.
MABlockClosure can do exactly this. But it may be overkill for whatever you need.
I know this has been solved but, for interested parties, I have another solution.
Remap the entire function to a new address space. The new resulting address can be used as a key to the required data.
#import <mach/mach_init.h>
#import <mach/vm_map.h>
void *remap_address(void* address, int page_count)
{
vm_address_t source_address = (vm_address_t) address;
vm_address_t source_page = source_address & ~PAGE_MASK;
vm_address_t destination_page = 0;
vm_prot_t cur_prot;
vm_prot_t max_prot;
kern_return_t status = vm_remap(mach_task_self(),
&destination_page,
PAGE_SIZE*(page_count ? page_count : 4),
0,
VM_FLAGS_ANYWHERE,
mach_task_self(),
source_page,
FALSE,
&cur_prot,
&max_prot,
VM_INHERIT_NONE);
if (status != KERN_SUCCESS)
{
return NULL;
}
vm_address_t destination_address = destination_page | (source_address & PAGE_MASK);
return (void*) destination_address;
}
Remember to handle pages that aren't required anymore and note that it takes a lot more memory per invocation than MABlockClosure.
(Tested on iOS)