-
Notifications
You must be signed in to change notification settings - Fork 43
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
C Blocks API #116
Comments
Blocks are a non-standard C extension so I don't think they should be in the header. Can they be decayed into a function and a void*? This would make it fairly easy to write a companion header with support for blocks. The current header is auto-generated with Dawn's code generators so it would be relatively easy to add a code generator for |
Yes. Support for blocks would be a convenience. |
Can you point to documentation on how to decay a block into a function pointer and
I think if we do this we could even explicitly make it Objective-C (a (Though at that point we've just created Objective-C WebGPU bindings. Which is good! 🙂) |
One approach is to cast a pointer to the block to |
Excellent point, I found people doing the opposite, but didn't think about that! |
Ping about adding a blocks API? Would be super useful... |
We could have a header for ObjC extensions but it's not clear how to do it:
|
(to be clear, blocks are not an ObjC extension; they're a C extension) |
It certainly is possible to do as suggested above, but real first-class blocks support would definitely be convenient. To be honest, I'm surprised at the amount of hesitation here; this is a non-breaking change, that is invisible from the perspective of someone who doesn't have the extension enabled in their compiler. It seems like only upside to me. |
IIUC they're, somewhat separately, an Objective-C built-in feature, a C extension, and a C++ extension. I think it's a fair question whether we should support niche C extensions, or if we should just have Objective-C bindings instead.
I read it as being a concern not from the user side, but from the implementation side. If it's in the header then all implementations need to implement it (when That said there is a strategy suggested above and it does seem to work. // modified from sample on wikipedia
#include <stdio.h>
#include <Block.h>
typedef int (^IntBlock)();
typedef int (*IntFunction)(void*);
IntBlock MakeCounter(int start, int increment) {
__block int i = start;
return Block_copy( ^(void) {
int ret = i;
i += increment;
return ret;
});
}
int callCCallback(IntFunction fn, void* userdata) {
return fn(userdata);
}
int runBlock(void* block_ptr) {
const IntBlock block = (IntBlock) block_ptr;
return block();
}
int main(void) {
IntBlock mycounter = MakeCounter(5, 2);
printf("Call 1: %d\n", callCCallback(runBlock, mycounter));
printf("Call 2: %d\n", callCCallback(runBlock, mycounter));
printf("Call 3: %d\n", callCCallback(runBlock, mycounter));
Block_release(mycounter);
return 0;
}
|
Forgot to say also: Since this won't affect existing entrypoints (would need new entrypoints) there's even less need for blocks in this header, vs just having a |
Using this approach requires manual memory management - explicit calls to I used this approach in WebKit/WebKit#14651. It works for the places where there's a one-shot callback (like
However, this doesn't work for multi-use callbacks, like The way this works with blocks is that the It used to be possible to work around this by having whichever object owns the WGPUDevice also retain the uncaptured error block, and release it at the same time as it releases the device. But this doesn't work any more now that WGPU objects are all reference-counted. There is no single owner, so there is no way for an observer to know when the WGPUDevice actually dies and it's safe to |
IIUC this only affects bindings. I think it's a non-issue to have to get the memory management right in the bindings code, which only has to be written once. It doesn't seem any different from having to get the memory management right in the webgpu.h implementation (of which there will be at least 3 - more than there will need to be objective-C or Clang C bindings). I think this can be handled in bindings just fine even in the UncapturedError case. Fundamentally, the C bindings have to provide the info needed to memory manage UncapturedError's userdata (which, here, is the Block itself), by guaranteeing that the UncapturedError callback is never called after the DeviceLost callback. So in the DeviceLost callback you can free the userdata (here, the Block). The bindings can listen for a DeviceLost callback even if the Objective-C application didn't ask for one. |
Is the DeviceLost callback supposed to fire even when the device dies of natural causes (its refcount goes to 0)? |
At least that's what happens in Dawn at least with |
All callbacks except UncapturedError are guaranteed to fire "eventually", so that the lifetime of the userdata pointer can be managed. In this case it's less clear to me because dropping the device doesn't necessarily mean it'll be freed - it may still have internal refs from other objects. But I'd say yes, it will get called, at least no later than when the actual internal refcount does reach 0 (you've cleaned up everything that references the device). |
C blocks are a more convenient way of handling callbacks. They automatically capture the relevant variables from the enclosing scope.
This:
could turn into this:
(Of course, typedefs could make this more readable.)
The text was updated successfully, but these errors were encountered: