I had the same reaction when I first encountered the "yield" command in ruby. Took a bit to wrap my brain around it, and suddenly I realized that it's a brilliant way to implement polymorphism. Same thing with code blocks.To me the use of blocks and delegates seem to fly in the original concepts of object programming.
How do I deal with Code Blocks?
-
- VIP Livecode Opensource Backer
- Posts: 3581
- Joined: Mon Jan 22, 2007 7:36 am
- Location: Berkeley, CA, US
- Contact:
Re: How do I deal with Code Blocks?
@Simon-
PowerDebug http://powerdebug.ahsoftware.net
PowerTools http://www.ahsoftware.net/PowerTools/PowerTools.irev
PowerTools http://www.ahsoftware.net/PowerTools/PowerTools.irev
Re: How do I deal with Code Blocks?
Just to get my two cents in hereTo me the use of blocks and delegates seem to fly in the original concepts of object programming
I'd actually argue blocks are at the very core of the original concepts of object (oriented) programming - SmallTalk being a good example here. Their existence meant that SmallTalk could be truly 'purely object oriented' - everything can be objects and methods, no primitive values nor explicit control flow syntax is required.
Consider the humble 'if' statement.
In most languages, this construct has to built into the language itself - there's no way to express its action in more fundamental terms (in that language). However, in SmallTalk you can build the construct from the ground up.
You define two objects True and False which respond differently to a message 'ifTrue:else:' (in pseudo-pure-object-oriented-language-code):
Code: Select all
True::ifTrue: (block)X else: (block)Y
{
X()
}
False::ifTrue: (block)X else: (block)Y
{
Y()
}
As long as all your predicates (testing methods) return an instance of True or an instance of False as appropriate, you have a functional (and perfectly natural!) 'if' statement defined in the language itself.
You can play this trick with any language which has some variant of this block-like concept (closures is probably a more general term to use).
Of course, such purity comes with a heavy cost - you need a very competent compiler or virtual machine to achieve similar performance to which you get from a built-in if construct!
-
- Posts: 636
- Joined: Wed Apr 24, 2013 4:53 pm
- Contact:
Re: How do I deal with Code Blocks?
Yes! Excellent news indeed!LCMark wrote: ↑Thu Mar 19, 2020 4:24 pmJust to let you know - we've taken the code which was hashed out on Gitter, cleaned it up, commented it and it will be included in the objc module from 9.6-DP-3 (PR is here: https://github.com/livecode/livecode/pull/7291). We've now used this code internally on both macOS and iOS so are reasonably confident it should work for many cases. The docs also contain example code fragments which are related to 'permissions requests' - so hopefully that will help with hooking into the calendar related permissions on iOS.
I've already done a few tests that make use of 9.6dp3's ObjCBlockPointer and it certainly makes things a bit more straight forward. The callbacks are firing when expected. Not sure why but I'm getting a lot of crashing as well, maybe threading issues? The crashes seem to be fairly random, after the callbacks have fired without a problem a bunch of times. I'm pretty sure I'm disposing of the blocks properly.
Here's a crash log, I had to get my callback to fire rapidly (as fast as I could click) about 20 times before getting this crash to happen:
Code: Select all
Application Specific Information:
abort() called
*** error for object 0x7f9e5415a1f0: pointer being freed was not allocated
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 libsystem_kernel.dylib 0x00007fffc6219d42 __pthread_kill + 10
1 libsystem_pthread.dylib 0x00007fffc6307457 pthread_kill + 90
2 libsystem_c.dylib 0x00007fffc617f4bb __abort + 140
3 libsystem_c.dylib 0x00007fffc617f42f abort + 144
4 libsystem_malloc.dylib 0x00007fffc626f07f free + 530
5 com.runrev.livecode 0x0000000109e1a7cf 0x109b1c000 + 3139535
6 com.runrev.livecode 0x0000000109d22267 0x109b1c000 + 2122343
7 com.runrev.livecode 0x0000000109c00f1c 0x109b1c000 + 937756
8 com.runrev.livecode 0x0000000109da7936 0x109b1c000 + 2668854
9 com.runrev.livecode 0x0000000109da2c72 0x109b1c000 + 2649202
10 com.runrev.livecode 0x0000000109da333e 0x109b1c000 + 2650942
11 com.runrev.livecode 0x0000000109db53b2 0x109b1c000 + 2724786
12 com.apple.Foundation 0x00007fffb2366c18 __NSFireDelayedPerform + 417
13 com.apple.CoreFoundation 0x00007fffb08ef364 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20
14 com.apple.CoreFoundation 0x00007fffb08eefef __CFRunLoopDoTimer + 1071
15 com.apple.CoreFoundation 0x00007fffb08eeb4a __CFRunLoopDoTimers + 298
16 com.apple.CoreFoundation 0x00007fffb08e6291 __CFRunLoopRun + 2065
17 com.apple.CoreFoundation 0x00007fffb08e5824 CFRunLoopRunSpecific + 420
18 com.apple.HIToolbox 0x00007fffafe45ebc RunCurrentEventLoopInMode + 240
19 com.apple.HIToolbox 0x00007fffafe45bf9 ReceiveNextEventCommon + 184
20 com.apple.HIToolbox 0x00007fffafe45b26 _BlockUntilNextEventMatchingListInModeWithFilter + 71
21 com.apple.AppKit 0x00007fffae3daa04 _DPSNextEvent + 1120
22 com.apple.AppKit 0x00007fffaeb567ee -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 2796
23 com.apple.AppKit 0x00007fffae3cf38b -[NSApplication run] + 926
24 com.runrev.livecode 0x0000000109db822b 0x109b1c000 + 2736683
25 libdyld.dylib 0x00007fffc60eb235 start + 1
Re: How do I deal with Code Blocks?
Hmmm looks like it could be because you are deleting the block pointer too soon - can you post your code?
-
- Posts: 636
- Joined: Wed Apr 24, 2013 4:53 pm
- Contact:
Re: How do I deal with Code Blocks?
That's what I was leaning towards, a zombie object somewhere, but I've tried moving it to various spots within the code, still haven't figured it out.
This is the main chunk of code that I've been trying to get right, a pretty simple callback that doesn't return any data:
https://github.com/PaulMcClernan/LCB_Ap ... #L47toL192
There's another one I had trouble with here, but haven't done any real detective work on yet:
https://github.com/PaulMcClernan/LCB_Co ... L582toL624
Both have test stacks. AVMIDIPlayerStopped callback fires when you hit play and then stop, if you do that rapidly a bunch of times in a row it crashes the engine.
CoreMIDISetupChanged callback fires when you create a Core MIDI Client and then change something about the system wide MIDI setup (like via Apples Audio MIDI Setup app). That Callback comes with a struct with some info about what was changed, the struct is setup via MCAggregateType="Jj".
Re: How do I deal with Code Blocks?
@PaulDaMacMan: I've taken a quick look at the AVMidi code... I wonder if:
Is the culprit - the pAVPlayerRef parameters should all be ObjcId... If you use ObjcRetainedId then the engine will 'give' its reference to the object to the method being called, meaning that you don't own it anymore. Usually you would use this when an ObjC method returns an object with a reference already added (e.g. Alloc methods).
Note: The Alloc method above should return ObjcRetainedId (IIRC) but the init method should take ObjcId and return ObjcId.
Perhaps see if changing those definitions above helps - I'll try to find some time to poke deeper later on.
Code: Select all
private foreign handler objC_AVMIDIPlayerAlloc() returns ObjcRetainedId binds to "objc:AVMIDIPlayer.+alloc"
private foreign handler objC_AVMIDIPlayer(in pAVMIDIPlayerInstance as ObjcRetainedId, in pFileNSURL as ObjcId, in pSoundFontNSURL as ObjcId, out pNSError as ObjcId) returns ObjcRetainedId binds to "objc:AVMIDIPlayer.-initWithContentsOfURL:soundBankURL:error:"
private foreign handler objC_AVMIDIPlayerPrepare(in pAVPlayerRef as ObjcRetainedId) returns nothing binds to "objc:AVMIDIPlayer.-prepareToPlay"
private foreign handler objC_AVMIDIPlayerPlay(in pAVPlayerRef as ObjcRetainedId, in pAVPlayDone as optional pointer) returns nothing binds to "objc:AVMIDIPlayer.-play:"
private foreign handler objC_AVMIDIPlayerStop(in pAVPlayerRef as ObjcRetainedId) returns nothing binds to "objc:AVMIDIPlayer.-stop"
private foreign handler objC_AVMIDIPlayerPlaying(in pAVPlayerRef as ObjcRetainedId) returns CBool binds to "objc:AVMIDIPlayer.isPlaying"
private foreign handler objC_AVMIDIPlayerRate(in pAVPlayerRef as ObjcRetainedId, in pRate as ObjcId) returns nothing binds to "objc:AVMIDIPlayer.rate"
private foreign handler objC_AVMIDIPlayerDuration(in pAVPlayerRef as ObjcRetainedId) returns NaturalFloat binds to "objc:AVMIDIPlayer.duration"
private foreign handler objC_AVMIDIPlayerCurrentPosition(in pAVPlayerRef as ObjcRetainedId) returns NaturalFloat binds to "objc:AVMIDIPlayer.currentPosition"
private foreign handler objC_AVMIDIPlayerSet(in pAVPlayerRef as ObjcRetainedId, in pValue as ObjcId, in pSelector as ObjcId) returns nothing binds to "objc:AVMIDIPlayer.-setValue:forKey:"
Note: The Alloc method above should return ObjcRetainedId (IIRC) but the init method should take ObjcId and return ObjcId.
Perhaps see if changing those definitions above helps - I'll try to find some time to poke deeper later on.
-
- Posts: 854
- Joined: Wed Nov 04, 2009 11:41 am
- Location: Gunthorpe, North Lincs, UK
Re: How do I deal with Code Blocks?
@LCMark
If I set the last instance of an object to empty will it always be destroyed ?
best wishes
Simon
So is it fair to say that except in exceptional circumstances this is the only time that OcjcRetainedID should be used ? I ask as I am not clear how the use of ObjcretainedID changes reference counting or even which part of the chain is doing the counting and controlling object destruction. I think that I do understand that the Alloc and Init methods should only result in an add 1 to the reference count. But there is also a great chance I have the wrong end of the stick again.Usually you would use this when an ObjC method returns an object with a reference already added (e.g. Alloc methods).
Note: The Alloc method above should return ObjcRetainedId (IIRC) but the init method should take ObjcId and return ObjcId.
If I set the last instance of an object to empty will it always be destroyed ?
best wishes
Simon
best wishes
Skids
Skids
-
- Posts: 636
- Joined: Wed Apr 24, 2013 4:53 pm
- Contact:
Re: How do I deal with Code Blocks?
Yes I probably changed those to RetainId while back, when I understood Retained a bit less, to see if object ownership had anything to do with the problems, coincidentaly I just changed those all (except for Alloc) to ObjCId last night while tying to zero out the Player object (limit it to one instance) before starting play again... It seems if you didn't issue a playStop before playing it would just keep making instances of the player (which is kind of fun).LCMark wrote: ↑Tue Mar 24, 2020 10:18 am@PaulDaMacMan: I've taken a quick look at the AVMidi code... I wonder if:Is the culprit - the pAVPlayerRef parameters should all be ObjcId... If you use ObjcRetainedId then the engine will 'give' its reference to the object to the method being called, meaning that you don't own it anymore. Usually you would use this when an ObjC method returns an object with a reference already added (e.g. Alloc methods).Code: Select all
private foreign handler objC_AVMIDIPlayerAlloc() returns ObjcRetainedId binds to "objc:AVMIDIPlayer.+alloc" private foreign handler objC_AVMIDIPlayerCurrentPosition(in pAVPlayerRef as ObjcRetainedId) returns NaturalFloat binds to "objc:AVMIDIPlayer.currentPosition" private foreign handler objC_AVMIDIPlayerSet(in pAVPlayerRef as ObjcRetainedId, in pValue as ObjcId, in pSelector as ObjcId) returns nothing binds to "objc:AVMIDIPlayer.-setValue:forKey:"
Note: The Alloc method above should return ObjcRetainedId (IIRC) but the init method should take ObjcId and return ObjcId.
Perhaps see if changing those definitions above helps - I'll try to find some time to poke deeper later on.
But the Callback still seems to crash the engine or doesn't get fired.
Thanks for looking.
Last edited by PaulDaMacMan on Tue Mar 24, 2020 9:39 pm, edited 1 time in total.
-
- Posts: 636
- Joined: Wed Apr 24, 2013 4:53 pm
- Contact:
Re: How do I deal with Code Blocks?
OK, I think I figured it out for the AVMIDIPlayerStopped calback and it was actually two things going on.
1) I had RetainedIds where I shouldn't have (which I guess was sometimes making Zombie player objects?)
2) My test stack has a "Stop" button that stoppeds an LCS position-check loop and flushes the message queue, so sometimes callback was probably firing but the message was getting flushed before it could be handled. If I hit the play button again it would issue the stop before playing again but without the message queue flushing and the callback worked every time!
Stress testing rapid play & stop it seems to be firing every time and the crashing is gone now too!
I'll have to remember to look at the old Message Watcher window when doing this stuff.
This means this lib can now be used without any loop that checks if the player's progress/position is passed the end position of the MIDI file (the player "position" would go on indefinitely otherwise), so this means it should now more easily be able to be used for background music for example, or used for playing a playlist of files in succession. Yeah!
Now on to the harder stuff, CoreMIDI callbacks!
1) I had RetainedIds where I shouldn't have (which I guess was sometimes making Zombie player objects?)
2) My test stack has a "Stop" button that stoppeds an LCS position-check loop and flushes the message queue, so sometimes callback was probably firing but the message was getting flushed before it could be handled. If I hit the play button again it would issue the stop before playing again but without the message queue flushing and the callback worked every time!
Stress testing rapid play & stop it seems to be firing every time and the crashing is gone now too!
I'll have to remember to look at the old Message Watcher window when doing this stuff.
This means this lib can now be used without any loop that checks if the player's progress/position is passed the end position of the MIDI file (the player "position" would go on indefinitely otherwise), so this means it should now more easily be able to be used for background music for example, or used for playing a playlist of files in succession. Yeah!
Now on to the harder stuff, CoreMIDI callbacks!
Last edited by PaulDaMacMan on Tue Mar 24, 2020 10:57 pm, edited 1 time in total.
-
- VIP Livecode Opensource Backer
- Posts: 3581
- Joined: Mon Jan 22, 2007 7:36 am
- Location: Berkeley, CA, US
- Contact:
Re: How do I deal with Code Blocks?
Hmmm... could you flush specific messages from the queue rather than brute-forcing it, i.e., leave the callback messages in place?
PowerDebug http://powerdebug.ahsoftware.net
PowerTools http://www.ahsoftware.net/PowerTools/PowerTools.irev
PowerTools http://www.ahsoftware.net/PowerTools/PowerTools.irev
-
- Posts: 636
- Joined: Wed Apr 24, 2013 4:53 pm
- Contact:
Re: How do I deal with Code Blocks?
Yeah, that would be a better way to flush messages to stop the check loop, but now that I know what the problem was I just moved the too-early flushing to later in the chain of events and that seems to work well enough for a test stack. Now that the callback is working well, if I didn't want to display the player's progress, I could just remove the check-loop altogether and just handle the AVMIDIPlayerStopped message if I want to do stuff when the MIDI file is done playing (like play the next file in a list).
-
- Posts: 636
- Joined: Wed Apr 24, 2013 4:53 pm
- Contact:
Re: How do I deal with Code Blocks?
So here's a crash-log from that other callback that's crashing (CoreMIDISetupChangedCallbackProc):
This one is particularly hard, when it crashes and I tell the system crash alert dialog to show the crash log sometimes it never does, at all, I mean no window with the Send the Crash Log to Apple button or anything!
Code: Select all
Crashed Thread: 0 Dispatch queue: com.apple.main-thread
Exception Type: EXC_BAD_ACCESS (SIGBUS)
Exception Codes: KERN_PROTECTION_FAILURE at 0x00007ff9447cb680
Exception Note: EXC_CORPSE_NOTIFY
VM Regions Near 0x7ff9447cb680:
MALLOC_TINY 00007ff944100000-00007ff944700000 [ 6144K] rw-/rwx SM=PRV
--> MALLOC_TINY 00007ff944700000-00007ff944800000 [ 1024K] rw-/rwx SM=ALI
MALLOC_TINY 00007ff944800000-00007ff944e00000 [ 6144K] rw-/rwx SM=PRV
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 ??? 0x00007ff9447cb680 0 + 140708572608128
1 com.apple.audio.midi.CoreMIDI 0x00007fff38918d91 _XNotify + 74
2 com.apple.audio.midi.CoreMIDI 0x00007fff38939fdb mshMIGPerform + 220
3 com.apple.CoreFoundation 0x00007fff37af82f7 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 41
4 com.apple.CoreFoundation 0x00007fff37af8255 __CFRunLoopDoSource1 + 527
5 com.apple.CoreFoundation 0x00007fff37ae024c __CFRunLoopRun + 2524
6 com.apple.CoreFoundation 0x00007fff37adf61e CFRunLoopRunSpecific + 455
7 com.apple.HIToolbox 0x00007fff36d3e1ab RunCurrentEventLoopInMode + 292
8 com.apple.HIToolbox 0x00007fff36d3dee5 ReceiveNextEventCommon + 603
9 com.apple.HIToolbox 0x00007fff36d3dc76 _BlockUntilNextEventMatchingListInModeWithFilter + 64
10 com.apple.AppKit 0x00007fff350d677d _DPSNextEvent + 1135
11 com.apple.AppKit 0x00007fff350d546b -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 1361
12 com.runrev.livecode 0x0000000101b04e94 0x10186b000 + 2727572
13 com.runrev.livecode 0x0000000101af69f8 0x10186b000 + 2669048
14 com.runrev.livecode 0x0000000101af1c72 0x10186b000 + 2649202
15 com.runrev.livecode 0x0000000101af233e 0x10186b000 + 2650942
16 com.runrev.livecode 0x0000000101b043b2 0x10186b000 + 2724786
17 com.apple.Foundation 0x00007fff39d8d2ba __NSFireDelayedPerform + 411
18 com.apple.CoreFoundation 0x00007fff37aff7c0 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20
19 com.apple.CoreFoundation 0x00007fff37aff36c __CFRunLoopDoTimer + 851
20 com.apple.CoreFoundation 0x00007fff37afeeb2 __CFRunLoopDoTimers + 330
21 com.apple.CoreFoundation 0x00007fff37ae00c2 __CFRunLoopRun + 2130
22 com.apple.CoreFoundation 0x00007fff37adf61e CFRunLoopRunSpecific + 455
23 com.apple.HIToolbox 0x00007fff36d3e1ab RunCurrentEventLoopInMode + 292
24 com.apple.HIToolbox 0x00007fff36d3dded ReceiveNextEventCommon + 355
25 com.apple.HIToolbox 0x00007fff36d3dc76 _BlockUntilNextEventMatchingListInModeWithFilter + 64
26 com.apple.AppKit 0x00007fff350d677d _DPSNextEvent + 1135
27 com.apple.AppKit 0x00007fff350d546b -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 1361
28 com.apple.AppKit 0x00007fff350cf588 -[NSApplication run] + 699
29 com.runrev.livecode 0x0000000101b0722b 0x10186b000 + 2736683
30 libdyld.dylib 0x00007fff63a353d5 start + 1