How do I deal with Code Blocks?

LiveCode Builder is a language for extending LiveCode's capabilities, creating new object types as Widgets, and libraries that access lower-level APIs in OSes, applications, and DLLs.

Moderators: LCMark, LCfraser

mwieder
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 2993
Joined: Mon Jan 22, 2007 7:36 am
Location: Berkeley, CA, US
Contact:

Re: How do I deal with Code Blocks?

Post by mwieder » Thu Mar 19, 2020 5:51 pm

@Simon-
To me the use of blocks and delegates seem to fly in the original concepts of object programming.
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.

LCMark
Livecode Staff Member
Livecode Staff Member
Posts: 1087
Joined: Thu Apr 11, 2013 11:27 am

Re: How do I deal with Code Blocks?

Post by LCMark » Thu Mar 19, 2020 7:26 pm

To me the use of blocks and delegates seem to fly in the original concepts of object programming
Just to get my two cents in here ;)

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()
}
[ Basically if you ask an instance of the True object 'ifTrue' it will execute the X block; and if you ask an instance of the False object 'ifTrue' it will execute the Y block. ]

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!

PaulDaMacMan
Posts: 259
Joined: Wed Apr 24, 2013 4:53 pm
Contact:

Re: How do I deal with Code Blocks?

Post by PaulDaMacMan » Mon Mar 23, 2020 8:35 am

LCMark wrote:
Thu Mar 19, 2020 4:24 pm
Just 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.
Yes! Excellent news indeed!

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
https://github.com/PaulMcClernan

LCMark
Livecode Staff Member
Livecode Staff Member
Posts: 1087
Joined: Thu Apr 11, 2013 11:27 am

Re: How do I deal with Code Blocks?

Post by LCMark » Mon Mar 23, 2020 1:50 pm

Hmmm looks like it could be because you are deleting the block pointer too soon - can you post your code?

PaulDaMacMan
Posts: 259
Joined: Wed Apr 24, 2013 4:53 pm
Contact:

Re: How do I deal with Code Blocks?

Post by PaulDaMacMan » Tue Mar 24, 2020 3:21 am

LCMark wrote:
Mon Mar 23, 2020 1:50 pm
Hmmm looks like it could be because you are deleting the block pointer too soon - can you post your code?
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".
https://github.com/PaulMcClernan

LCMark
Livecode Staff Member
Livecode Staff Member
Posts: 1087
Joined: Thu Apr 11, 2013 11:27 am

Re: How do I deal with Code Blocks?

Post by LCMark » Tue Mar 24, 2020 10:18 am

@PaulDaMacMan: I've taken a quick look at the AVMidi code... I wonder if:

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:"
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.

Simon Knight
Posts: 543
Joined: Wed Nov 04, 2009 11:41 am
Location: Gunthorpe, North Lincs, UK

Re: How do I deal with Code Blocks?

Post by Simon Knight » Tue Mar 24, 2020 12:15 pm

@LCMark
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.
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.

If I set the last instance of an object to empty will it always be destroyed ?

best wishes

Simon
best wishes
Skids

PaulDaMacMan
Posts: 259
Joined: Wed Apr 24, 2013 4:53 pm
Contact:

Re: How do I deal with Code Blocks?

Post by PaulDaMacMan » Tue Mar 24, 2020 7:17 pm

LCMark wrote:
Tue Mar 24, 2020 10:18 am
@PaulDaMacMan: I've taken a quick look at the AVMidi code... I wonder if:

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:"
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.
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).
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.
https://github.com/PaulMcClernan

PaulDaMacMan
Posts: 259
Joined: Wed Apr 24, 2013 4:53 pm
Contact:

Re: How do I deal with Code Blocks?

Post by PaulDaMacMan » Tue Mar 24, 2020 9:28 pm

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!
Last edited by PaulDaMacMan on Tue Mar 24, 2020 10:57 pm, edited 1 time in total.
https://github.com/PaulMcClernan

mwieder
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 2993
Joined: Mon Jan 22, 2007 7:36 am
Location: Berkeley, CA, US
Contact:

Re: How do I deal with Code Blocks?

Post by mwieder » Tue Mar 24, 2020 9:53 pm

Hmmm... could you flush specific messages from the queue rather than brute-forcing it, i.e., leave the callback messages in place?

PaulDaMacMan
Posts: 259
Joined: Wed Apr 24, 2013 4:53 pm
Contact:

Re: How do I deal with Code Blocks?

Post by PaulDaMacMan » Tue Mar 24, 2020 11:03 pm

mwieder wrote:
Tue Mar 24, 2020 9:53 pm
Hmmm... could you flush specific messages from the queue rather than brute-forcing it, i.e., leave the callback messages in place?
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).
https://github.com/PaulMcClernan

PaulDaMacMan
Posts: 259
Joined: Wed Apr 24, 2013 4:53 pm
Contact:

Re: How do I deal with Code Blocks?

Post by PaulDaMacMan » Wed Mar 25, 2020 2:28 am

So here's a crash-log from that other callback that's crashing (CoreMIDISetupChangedCallbackProc):

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
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!
https://github.com/PaulMcClernan

Post Reply

Return to “LiveCode Builder”