I'm using Windows XP
Flash player 11,3,300,257
“A script in this movie is causing Adobe Flash Player to run slowly. If it continues to run, your computer may become unresponsive. Do you want to abort the script?”
public function getMembersCount(channel:String=''):*{
if(typeof netGroups[channel]=='undefined') {
return;
}
return netGroups[channel].netGroup.estimatedMemberCount;
}
Launching this code from js every 3 seconds via setInterval via ExternalInterface. After 80 intervals it drops this popup window with that message, blocking every flash application in all Google Chrome tabs, even youtube stops playing!
Interesting, calling this function doesn't trigger popup even after 179 times:
public function helloworld(str:String=''):*{
return 'Hello world!';
}
So the problem is in NetGroup itself or ExternalInterface?
How to avoid this popup?
It seems there is some threshold check somewhere. Popup appears after exactly 46th iteration, if i'm stopping iterations manualy after 45 iteration this popup isn't displayed.
If i'm stopping iterations after 45 iteration even for 10 minutes and then resuming it's throwing this popup window right away.
Tools for debugging this will be welcome.
Built-in debugger should be ok, Flash Builder debugger would be better. Something is feeding data into 'netGroups'. The fact that a 'get' function on an objects property is causing that is very unlikely.
What exactly is netGroups a parsed data structure of (how large is this NetGroup) and is whatever is populating and updating it (unless the default) interfacing with other services whether it be over TCP, UDP, HTTP->Script (esp database), etc?
Are you listening for all netStatus events from the NetGroup?
Debugger doesn't drop any exceptions.
public var netGroups:Object = new Object();
s is s:Object options from JS for group creation
next is:
var Group:Object = new Object();
Group.filters=s.filters;
Group.specifier = new GroupSpecifier('some_name');
Group.netGroup = new NetGroup(netConnection,Group.specifier.groupspecWithAuthorizations()) ;
Group.netGroup.addEventListener(NetStatusEvent.NET_STATUS,netStatus);
// /** Save group **/
netGroups[Group.channel] = Group;
NetGroups just store NetGroup instances to avoid garbage collection and easier access.
Nothing is updating NetGroup, i'll check everything related to them and to collection itself.
I'm listening to
NetGroup.Connect.Success
NetGroup.Connect.Rejected
NetGroup.Connect.Failed
NetGroup.Posting.Notify
NetGroup.Neighbor.Connect
NetGroup.Neighbor.Disconnect
NetGroup.SendTo.Notify
Not listening to replication events and multicast, because don't use them yet.
Only NetGroup.LocalCoverage.Notify is empty. Don't know what exactly description in documentation means...
"Sent when a portion of the group address space for which this node is responsible changes."
Anyway, why is this popup window is appearing? What are the criteria, circumstances of it's appearance?
You're halting Flash from continuing to process frames. Infinite loops or recursion, network connect timeouts and trying to execute excessive amounts of code on a single frame will bring up the dialog. There is a setting in Flash Pro publish settings for this dialog (File->Publish Settings->Flash (.swf) Settings->Script time limit (15 seconds by default).
If a network connection is attempted (especially on windows) that fails and a timeout exceeds 15 seconds the script may halt. I've hit this walls tons if I attempt to use sockets and don't set the timeout much higher.
In your situation I would absolutely validate a response returned from flash and never request information again until I get a valid response. Flash may delay sending a result or you're not validating it and your loop just continues to mercilessly ask Flash for information but Flash won't respond and then, there's your dialog.
Found where to change it in Flash Builder, it's in Project->Properties->ActionScript Compiler settings->additional compiler arguments field below:
-locale en_US -default-script-limits 1000 10
You are probably right. Reading this description:
The max-execution-time
value specifies the maximum duration, in seconds, that an ActionScript event handler can execute before Flash Player assumes that it is hung, and aborts it. The default value is 60 seconds. You cannot set this value above 60 seconds.
It's these 60 seconds i was talking above! The reason probably in javascript async nature of setInterval() it doesn't wait for complete of previous interval and starts new one. Guess i have to reconsider this and use direct call to made it sync.
Another question then. How to abort current call of External Interface if it hangs for example more than 50 seconds to avoid this popup appearing?
Will reply here when i'll remake my code.
It's all about validation. I'm not extremely familiar with the NetUser class but most network libs come with a timeout property on any connection or query. Networks are notorious for locking things up, they require timeouts. It's always standard practice to make a request and then handle all possible contingencies. If you get a good result, continue, if not, handle the part of the problem that went wrong based on the information you have on hand. If you handle it and it continues to fail, warn the user there is a network issue and to either reconnect or try again later.
Pretend I'm asking you. Do you think I should ever do something infinitely that never validates? Could anything go wrong? ![]()
I have recompiled my flash app with -default-script-limits 1000 10 for easier testing. Changed js code to wait until sync communication with flash will end and return result(without setInterval, used setTimeout at the end of each call), checked returned result as intended, but it anyway drops popup. Now after 10 seconds as i have set in default-script-limits 1000 10!
I think i have found the issue, but not completely. I have reduced returning data from flash->to->js to minimum. I was returning whole (NetGroup instance+GroupSpec+some addional init params stored in it) now I have reduced to required estimatedMemberCount and it seems it's gone for now! 260+ setTimeout(3000) iterations and no popup, even with new 10 sec timeout compile setting.
So it seems the problem in somewhere inside ExternalInterface. It seems it have problems transfering large objects to JS.
As i understand communication is based on toXML -> toJS -> parseXML -> launch in JS with apply(context,arguments)?
Need to find the real issue, so i can avoid further problem with this behavior.
I have sent enormous amounts of data via ExternalInterface and never had an issue. Although I always base64 encode my data and wrap it in a format like JSON before sending. There may be a character that's causing the response to fail that encoding and wrapping may solve. ExternalInterface either goes over ActiveX or (from Adobe docs) uses this API:
http://www.mozilla.org/projects/plugins/npruntime.html
That API itself may have a limit but the ExternalInterface documentation doesn't specify any limits. It also states any ActionScript types are converted automatically. Perhaps it's having an issue converting a type that isn't a base ActionScript or JavaScript type. Again, encoding and wrapping the objects up in JSON would solve an issue like that.
As for testing limits, you can write a quick test app that sends info to JS via ExternalInterface, continually appending more and more data (I'd do it in large chunks) until you see it fail. It should be pretty quick and easy to find any limits like that. I still think your issue is unrelated to size more than it's related to translation of a character or type in transmission back to JS.
You can also do some basic stuff like watch the memory footprint of your browser while the app runs. If you continually allocate more and more ram then you should see how you're disposing unneeded objects. JS may be allocating to the point your browser simply stops it from functioning properly due to mass allocation. I see no immediate options in say Firefox's Advanced Javascript Options though so I think memory usage is fairly liberal. If it becomes a problem you can find a more efficient binary data type to use as well which compresses data down (the purpose of AMF for instance). LocalConnection has around a 100kb limit if I remember correctly so a limit somewhere in your path is possible.
I think this "it's related to translation of a character or type in transmission back to JS" is the reason.
But that doesn't explain why it's working and then suddenly not, Netgroup isn't changed, only Netgroup or GroupSpec instance is making some changes of itself on the background.
I'll check if NetGroup is the reason.
If it walks like a duck, sounds like a duck and looks like a duck, it's probably a duck.
If you test transmitting something else over ExternalInterface instead of the NetGroup object and it never fails, it's the NetGroup object. What part of it I'm not sure you'll ever know.
I'd still advise converting it into a base64 encoded friendly wrapped JSON object and unwrapping it on the JS end. Those multibyte characters and accidental XML invalidators get me all the time (like auto-converting & to & invalidating XML markup rules). So I just convert everything.
Because you're using such a fast interval (3 seconds), using a binary wrapper format could kill 2 birds with one stone. Reduce bandwidth and eliminate character issues. Action Message Format (AMF, http://en.wikipedia.org/wiki/Action_Message_Format ) might be ideal, native to AS3. You'll need a JavaScript AMF library too ( https://github.com/jamesward/JSAMF ).
I have tried to convert NetGroup into JSON like that:
public function getChannelInfo(channel:String=''):*{
return com.adobe.serialization.json.JSON.encode(netGroups[channel].netGroup) ;
}
JS console.log(result) is:
{"receiveMode":"exact","replicationStrategy":"lowestFirst","estimatedM emberCount":1,"neighborCount":0,"localCoverageFrom":"86254b32dbf8693fd 6864b3fa057088122906933870ee12d48194b0c18a2b3df","localCoverageTo":"86 254b32dbf8693fd6864b3fa057088122906933870ee12d48194b0c18a2b3df","info" :{"postingReceiveControlBytesPerSecond":0,"routingSendBytesPerSecond": 0,"routingReceiveBytesPerSecond":0,"objectReplicationSendBytesPerSecon d":0,"objectReplicationReceiveBytesPerSecond":0,"postingSendDataBytesP erSecond":0,"postingSendControlBytesPerSecond":0,"postingReceiveDataBy tesPerSecond":0}}
And anyway after 13~ calls with 3 seconds interval it throws popup window...
Here it is:
public function getChannelInfo(channel:String=''):String{
var mb:ByteArray=new ByteArray();
mb.writeMultiByte(com.adobe.serialization.json.JSON.encode(netGr oups[channel].netGroup),"utf-8");
return by.blooddy.crypto.Base64.encode(mb);
}
I'm using one of the external libs, it's very fast.
Results:
eyJyZWNlaXZlTW9kZSI6ImV4YWN0IiwicmVwbGljYXRpb25TdHJhdGVneSI6Imxvd2VzdE ZpcnN0IiwiZXN0aW1hdGVkTWVtYmVyQ291bnQiOjEsIm5laWdoYm9yQ291bnQiOjAsImxv Y2FsQ292ZXJhZ2VGcm9tIjoiZDc0YzZjYjRlODAyN2RjMmIyNjIzZjI4NjhjY2VlNzIyZj E1NjY3ZGMyNWYwYTE3OTM1NWNjN2U3NWRmMzMwNiIsImxvY2FsQ292ZXJhZ2VUbyI6ImQ3 NGM2Y2I0ZTgwMjdkYzJiMjYyM2YyODY4Y2NlZTcyMmYxNTY2N2RjMjVmMGExNzkzNTVjYz dlNzVkZjMzMDYiLCJpbmZvIjp7InBvc3RpbmdSZWNlaXZlQ29udHJvbEJ5dGVzUGVyU2Vj b25kIjowLCJyb3V0aW5nU2VuZEJ5dGVzUGVyU2Vjb25kIjowLCJyb3V0aW5nUmVjZWl2ZU J5dGVzUGVyU2Vjb25kIjowLCJvYmplY3RSZXBsaWNhdGlvblNlbmRCeXRlc1BlclNlY29u ZCI6MCwib2JqZWN0UmVwbGljYXRpb25SZWNlaXZlQnl0ZXNQZXJTZWNvbmQiOjAsInBvc3 RpbmdTZW5kRGF0YUJ5dGVzUGVyU2Vjb25kIjowLCJwb3N0aW5nU2VuZENvbnRyb2xCeXRl c1BlclNlY29uZCI6MCwicG9zdGluZ1JlY2VpdmVEYXRhQnl0ZXNQZXJTZWNvbmQiOjB9fQ ==
The funny thing is, that when i'm replacing NetGroup with 73kb long json string, it's working and popup doesn't appears!
public function getChannelInfo(channel:String=''):String{
var mb:ByteArray=new ByteArray();
mb.writeMultiByte('veeeeeery long json serialized string here 73kb overall',"utf-8");
return by.blooddy.crypto.Base64.encode(mb);
}
So it seems Flash runtime refuses or have difficulties converting or allowing of conversion of some data inside NetGroup.
Seems to be narrowing down nicely to a type or acccess issue. When you request the data does NetGroup query anything or is it returning information it already contains? I'm not familiar enough with it to know it that in-depth.
If it's querying something then it could be waiting on that query, or failing it. The target of the query could also have an anti-hammer policy. If too many requests happen in a designated period some servers are configured to ignore the request or warn the user not to hammer the server.
If the data is being populated by other services automatically and no queries are made then I'd suspect you are indeed having an issue converting a type.
You can always go old school, make your own "type" (class) that contains the information you really need and populate it from the NetGroup object yourself, then send just that data. It's a lot of work but I don't know how mission critical it is. I expect eventually you'll add in a piece from the NG object and it all breaks again. Then you'll know what part wasn't working. Also with your own type you can put a little padding between you and the NG object for some better fallback validation and protection against these odd halts.
Don't forget, AMF might solve it.
I'll check with AMF3 tomorrow.
But this solved it!
public function getMembersCount(channel:String=''):*{
try {
if(!membersCount[channel]) {
membersCount[channel]=0;
setInterval(function():void{
membersCount[channel]=netGroups[channel].netGroup .estimatedMemberCount;
},1000);
}
return membersCount[channel];
}
catch(e:*){
log('Failed to getMembersCount',e);
}
}
Need some more love and interval id storage, but it's working and doesn't throw that stupid popup window ffs!
It seems there are really happening some actions on background with estimatedMemberCount acquiring. It calculates current members in the p2p network.
All queries over a network need to be roped in. You can never expect them to go right all the time, there's just too many factors.
It may not cause the player to pop a dialog but you are leaving some queries running until a timeout hits which at a pace of "once per 3 seconds" can add up.
If it's a failed network connection AMF3 won't solve that. You just always needed to validate the response, or that you even received one
.
It's not something readily accessible but your playerglobal.swc is just a .zip file (as all .FLA / .SWC / .IPA / etc are). Unzip it and grab library.swf. If you decompile that SWF you will be able to look through the code. I'm not sure if it's frowned upon but I wouldn't see why, aside decompiling SWF for "the wrong reasons". I don't find this to be a wrong reason when it's necessary sometimes to understand something from the ground up.
Do note, just like JavaScript minification, some AS3 may be obfuscated and difficult to follow. Some isn't. How difficult any specific library is to read is arbitrary. That would be how you can take a look however.
North America
Europe, Middle East and Africa
Asia Pacific