You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@flex.apache.org by Michael Schwarz <mi...@googlemail.com> on 2017/10/09 07:22:30 UTC

Background Worker disposing of failed MessageChannel freezes application

Hi there,

I encountered a really strange behavior with Flex and already tried
StackOverflow and Reddit, but didn't get any reponse, so I'm trying here
again:

I have the following scenario: I am using a MessageChannel to communicate
with a background worker in a flex application. Since the code is to
eventually be transformed into a library, it should also be able to handle
malformed input (e.g. sending classes via the channel for which no class
alias is registered in the background worker). In this case I want to abort
the worker. My code is the following:

package{
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.system.MessageChannel;
    import flash.system.Worker;

    public class BW extends Sprite
    {
        /** Incoming channel */
        private var fromCoordinatorChannel:MessageChannel;

        /** Outgoing channel */
        private var toCoordinatorChannel:MessageChannel;

        public function BW()
        {
            super();
            initChannels();
        }

        /**
         * Get channels from shared property and attach event listener
         */
        private function initChannels():void {
            // Get channnels from shared property
            fromCoordinatorChannel =
Worker.current.getSharedProperty("toWorkerChannel");
            toCoordinatorChannel =
Worker.current.getSharedProperty("fromWorkerChannel");

            // Attach event listener for incoming messages
            fromCoordinatorChannel.addEventListener(Event.CHANNEL_MESSAGE,
onIncomingMessage);
        }

        /**
         * Event handler for incoming messages on the channel.
         * @param event Event that came in
         */
        private function onIncomingMessage(event:Event):void {
            handleIncoming();
        }

        /**
         * Get oldest message from channel and handle it
         */
        private function handleIncoming():void {
            if(fromCoordinatorChannel.messageAvailable) {
                try {
                    var wm:Object = fromCoordinatorChannel.receive(true);
                } catch(e:Error) {
                    fromCoordinatorChannel.close();
                    trace("Invalid type of package sent - could not be
deserialized.");

                    // Kill myself
                    fromCoordinatorChannel = null;
                    toCoordinatorChannel = null;
                    Worker.current.setSharedProperty("toWorkerChannel", null);
                    Worker.current.setSharedProperty("fromWorkerChannel", null);

                    Worker.current.terminate();
                }
            }
        }
    }}

And in the primordial worker:

<?xml version="1.0" encoding="utf-8"?><s:WindowedApplication
xmlns:fx="http://ns.adobe.com/mxml/2009"
                       xmlns:s="library://ns.adobe.com/flex/spark"
                       xmlns:mx="library://ns.adobe.com/flex/mx">
    <fx:Script>
        <![CDATA[
            import mx.events.FlexEvent;

            var worker:Worker;
            var to:MessageChannel;
            var from:MessageChannel;

            var graveyard:Array = new Array();


            private function removeWorkerIfFailed():void {
                if(worker && worker.state == WorkerState.TERMINATED) {
                    from.close();
                    worker = null;

                    // What the actual f***? If I allow this channel
to be garbage collected, it breaks. If I prevent that, it doesn't
(o.Ó)
                    graveyard.push(to);

                    to = null;
                    from = null;
                }
            }


            protected function button1_clickHandler(event:MouseEvent):void
            {
                registerClassAlias("Example", Example);

                // Create worker and channels
                worker = WorkerDomain.current.createWorker(Workers.BW);
                to = Worker.current.createMessageChannel(worker);
                from = worker.createMessageChannel(Worker.current);

                // Attach event listener to status of worker so its
reference can be deleted when it fails

worker.addEventListener(Event.WORKER_STATE,function(event:Event):void
{removeWorkerIfFailed();});

                // Set shared properties so worker can access channels
                worker.setSharedProperty("toWorkerChannel", to);
                worker.setSharedProperty("fromWorkerChannel", from);

                // Attach event listener for incoming messages
                from.addEventListener(Event.CHANNEL_MESSAGE,
function(event:Event):void { trace('incoming'); });

                // Start the worker
                worker.start();


                var example1:Example = new Example("one");
                to.send(example1);
            }
        ]]>
    </fx:Script>
    <s:Button label="Do it" click="button1_clickHandler(event)">

    </s:Button></s:WindowedApplication>

Add the Example class

package{
    import flash.utils.IDataInput;
    import flash.utils.IDataOutput;
    import flash.utils.IExternalizable;

    public class Example implements IExternalizable
    {
        public var name:String;
        public function Example(name:String)
        {
            this.name = name;
        }

        public function readExternal(input:IDataInput):void
        {
            name = input.readUTF();
        }

        public function writeExternal(output:IDataOutput):void
        {
            output.writeUTF(name);
        }

    }}

The problem is the following: If I remove the line in the
removeWorkerIfFailed() that pushes a reference to the array (thereby
preventing the channel from being garbage collected), the main application
freezes. The debugger does not show any active function calls. As long as
that line is there, everything works fine.

To reiterate: I know that in order to fix it, I need to call the
registerClassAlias(...) also in the background worker, but I am trying to
handle precisely this case that someone throws something wrong at the
background worker.

I'm really thankful for any pointers

Thanks,

Michael

Re: Background Worker disposing of failed MessageChannel freezes application

Posted by Alex Harui <ah...@adobe.com.INVALID>.
Hi,

I was hoping someone with experience with workers would answer.  I haven't
worked with them myself.

Is it possible for the worker to not kill itself in an event handler, or
at least, in a CHANNEL_MESSAGE handler?  Maybe set a flag to stop doing
more work and either kill itself 'later' or ask the main thread to kill it?

Maybe the Flash runtime isn't prepared for a worker to go away in certain
situations.

HTH,
-Alex

On 10/15/17, 6:20 AM, "Michael Schwarz" <mi...@googlemail.com>
wrote:

>Hi everyone,
>
>sorry for resending this, but I still have the same issue.
>Is there another mailing list or online community you could recommend that
>might have an answer to this question? Or is there a way to improve my
>question to make it easier to help?
>Thanks,
>
>Michael
>
>On Mon, Oct 9, 2017 at 9:22 AM Michael Schwarz <
>michael.schwarz93@googlemail.com> wrote:
>
>> Hi there,
>>
>> I encountered a really strange behavior with Flex and already tried
>> StackOverflow and Reddit, but didn't get any reponse, so I'm trying here
>> again:
>>
>> I have the following scenario: I am using a MessageChannel to
>>communicate
>> with a background worker in a flex application. Since the code is to
>> eventually be transformed into a library, it should also be able to
>>handle
>> malformed input (e.g. sending classes via the channel for which no class
>> alias is registered in the background worker). In this case I want to
>>abort
>> the worker. My code is the following:
>>
>> package{
>>     import flash.display.Sprite;
>>     import flash.events.Event;
>>     import flash.system.MessageChannel;
>>     import flash.system.Worker;
>>
>>     public class BW extends Sprite
>>     {
>>         /** Incoming channel */
>>         private var fromCoordinatorChannel:MessageChannel;
>>
>>         /** Outgoing channel */
>>         private var toCoordinatorChannel:MessageChannel;
>>
>>         public function BW()
>>         {
>>             super();
>>             initChannels();
>>         }
>>
>>         /**
>>          * Get channels from shared property and attach event listener
>>          */
>>         private function initChannels():void {
>>             // Get channnels from shared property
>>             fromCoordinatorChannel =
>>Worker.current.getSharedProperty("toWorkerChannel");
>>             toCoordinatorChannel =
>>Worker.current.getSharedProperty("fromWorkerChannel");
>>
>>             // Attach event listener for incoming messages
>>             
>>fromCoordinatorChannel.addEventListener(Event.CHANNEL_MESSAGE,
>>onIncomingMessage);
>>         }
>>
>>         /**
>>          * Event handler for incoming messages on the channel.
>>          * @param event Event that came in
>>          */
>>         private function onIncomingMessage(event:Event):void {
>>             handleIncoming();
>>         }
>>
>>         /**
>>          * Get oldest message from channel and handle it
>>          */
>>         private function handleIncoming():void {
>>             if(fromCoordinatorChannel.messageAvailable) {
>>                 try {
>>                     var wm:Object =
>>fromCoordinatorChannel.receive(true);
>>                 } catch(e:Error) {
>>                     fromCoordinatorChannel.close();
>>                     trace("Invalid type of package sent - could not be
>>deserialized.");
>>
>>                     // Kill myself
>>                     fromCoordinatorChannel = null;
>>                     toCoordinatorChannel = null;
>>                     Worker.current.setSharedProperty("toWorkerChannel",
>>null);
>>                 
>>Worker.current.setSharedProperty("fromWorkerChannel", null);
>>
>>                     Worker.current.terminate();
>>                 }
>>             }
>>         }
>>     }}
>>
>> And in the primordial worker:
>>
>> <?xml version="1.0" encoding="utf-8"?><s:WindowedApplication
>>xmlns:fx="https://na01.safelinks.protection.outlook.com/?url=http%3A%2F%2
>>Fns.adobe.com%2Fmxml%2F2009&data=02%7C01%7C%7C8210af64a1fe4e2a8f2808d513c
>>f93fd%7Cfa7b1b5a7b34438794aed2c178decee1%7C0%7C0%7C636436704640671187&sda
>>ta=Kjlgy55n9mnR9aUt9aAQuMy5%2BIMUd%2F8ZWUp1E8ZLF%2Fg%3D&reserved=0"
>>                        xmlns:s="library://ns.adobe.com/flex/spark"
>>                        xmlns:mx="library://ns.adobe.com/flex/mx">
>>     <fx:Script>
>>         <![CDATA[
>>             import mx.events.FlexEvent;
>>
>>             var worker:Worker;
>>             var to:MessageChannel;
>>             var from:MessageChannel;
>>
>>             var graveyard:Array = new Array();
>>
>>
>>             private function removeWorkerIfFailed():void {
>>                 if(worker && worker.state == WorkerState.TERMINATED) {
>>                     from.close();
>>                     worker = null;
>>
>>                     // What the actual f***? If I allow this channel to
>>be garbage collected, it breaks. If I prevent that, it doesn't (o.Ó)
>>                     graveyard.push(to);
>>
>>                     to = null;
>>                     from = null;
>>                 }
>>             }
>>
>>
>>             protected function
>>button1_clickHandler(event:MouseEvent):void
>>             {
>>                 registerClassAlias("Example", Example);
>>
>>                 // Create worker and channels
>>                 worker = WorkerDomain.current.createWorker(Workers.BW);
>>                 to = Worker.current.createMessageChannel(worker);
>>                 from = worker.createMessageChannel(Worker.current);
>>
>>                 // Attach event listener to status of worker so its
>>reference can be deleted when it fails
>>                 
>>worker.addEventListener(Event.WORKER_STATE,function(event:Event):void
>>{removeWorkerIfFailed();});
>>
>>                 // Set shared properties so worker can access channels
>>                 worker.setSharedProperty("toWorkerChannel", to);
>>                 worker.setSharedProperty("fromWorkerChannel", from);
>>
>>                 // Attach event listener for incoming messages
>>                 from.addEventListener(Event.CHANNEL_MESSAGE,
>>function(event:Event):void { trace('incoming'); });
>>
>>                 // Start the worker
>>                 worker.start();
>>
>>
>>                 var example1:Example = new Example("one");
>>                 to.send(example1);
>>             }
>>         ]]>
>>     </fx:Script>
>>     <s:Button label="Do it" click="button1_clickHandler(event)">
>>
>>     </s:Button></s:WindowedApplication>
>>
>> Add the Example class
>>
>> package{
>>     import flash.utils.IDataInput;
>>     import flash.utils.IDataOutput;
>>     import flash.utils.IExternalizable;
>>
>>     public class Example implements IExternalizable
>>     {
>>         public var name:String;
>>         public function Example(name
>>
>>


Re: Background Worker disposing of failed MessageChannel freezes application

Posted by Michael Schwarz <mi...@googlemail.com>.
Hi everyone,

sorry for resending this, but I still have the same issue.
Is there another mailing list or online community you could recommend that
might have an answer to this question? Or is there a way to improve my
question to make it easier to help?
Thanks,

Michael

On Mon, Oct 9, 2017 at 9:22 AM Michael Schwarz <
michael.schwarz93@googlemail.com> wrote:

> Hi there,
>
> I encountered a really strange behavior with Flex and already tried
> StackOverflow and Reddit, but didn't get any reponse, so I'm trying here
> again:
>
> I have the following scenario: I am using a MessageChannel to communicate
> with a background worker in a flex application. Since the code is to
> eventually be transformed into a library, it should also be able to handle
> malformed input (e.g. sending classes via the channel for which no class
> alias is registered in the background worker). In this case I want to abort
> the worker. My code is the following:
>
> package{
>     import flash.display.Sprite;
>     import flash.events.Event;
>     import flash.system.MessageChannel;
>     import flash.system.Worker;
>
>     public class BW extends Sprite
>     {
>         /** Incoming channel */
>         private var fromCoordinatorChannel:MessageChannel;
>
>         /** Outgoing channel */
>         private var toCoordinatorChannel:MessageChannel;
>
>         public function BW()
>         {
>             super();
>             initChannels();
>         }
>
>         /**
>          * Get channels from shared property and attach event listener
>          */
>         private function initChannels():void {
>             // Get channnels from shared property
>             fromCoordinatorChannel = Worker.current.getSharedProperty("toWorkerChannel");
>             toCoordinatorChannel = Worker.current.getSharedProperty("fromWorkerChannel");
>
>             // Attach event listener for incoming messages
>             fromCoordinatorChannel.addEventListener(Event.CHANNEL_MESSAGE, onIncomingMessage);
>         }
>
>         /**
>          * Event handler for incoming messages on the channel.
>          * @param event Event that came in
>          */
>         private function onIncomingMessage(event:Event):void {
>             handleIncoming();
>         }
>
>         /**
>          * Get oldest message from channel and handle it
>          */
>         private function handleIncoming():void {
>             if(fromCoordinatorChannel.messageAvailable) {
>                 try {
>                     var wm:Object = fromCoordinatorChannel.receive(true);
>                 } catch(e:Error) {
>                     fromCoordinatorChannel.close();
>                     trace("Invalid type of package sent - could not be deserialized.");
>
>                     // Kill myself
>                     fromCoordinatorChannel = null;
>                     toCoordinatorChannel = null;
>                     Worker.current.setSharedProperty("toWorkerChannel", null);
>                     Worker.current.setSharedProperty("fromWorkerChannel", null);
>
>                     Worker.current.terminate();
>                 }
>             }
>         }
>     }}
>
> And in the primordial worker:
>
> <?xml version="1.0" encoding="utf-8"?><s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
>                        xmlns:s="library://ns.adobe.com/flex/spark"
>                        xmlns:mx="library://ns.adobe.com/flex/mx">
>     <fx:Script>
>         <![CDATA[
>             import mx.events.FlexEvent;
>
>             var worker:Worker;
>             var to:MessageChannel;
>             var from:MessageChannel;
>
>             var graveyard:Array = new Array();
>
>
>             private function removeWorkerIfFailed():void {
>                 if(worker && worker.state == WorkerState.TERMINATED) {
>                     from.close();
>                     worker = null;
>
>                     // What the actual f***? If I allow this channel to be garbage collected, it breaks. If I prevent that, it doesn't (o.Ó)
>                     graveyard.push(to);
>
>                     to = null;
>                     from = null;
>                 }
>             }
>
>
>             protected function button1_clickHandler(event:MouseEvent):void
>             {
>                 registerClassAlias("Example", Example);
>
>                 // Create worker and channels
>                 worker = WorkerDomain.current.createWorker(Workers.BW);
>                 to = Worker.current.createMessageChannel(worker);
>                 from = worker.createMessageChannel(Worker.current);
>
>                 // Attach event listener to status of worker so its reference can be deleted when it fails
>                 worker.addEventListener(Event.WORKER_STATE,function(event:Event):void {removeWorkerIfFailed();});
>
>                 // Set shared properties so worker can access channels
>                 worker.setSharedProperty("toWorkerChannel", to);
>                 worker.setSharedProperty("fromWorkerChannel", from);
>
>                 // Attach event listener for incoming messages
>                 from.addEventListener(Event.CHANNEL_MESSAGE, function(event:Event):void { trace('incoming'); });
>
>                 // Start the worker
>                 worker.start();
>
>
>                 var example1:Example = new Example("one");
>                 to.send(example1);
>             }
>         ]]>
>     </fx:Script>
>     <s:Button label="Do it" click="button1_clickHandler(event)">
>
>     </s:Button></s:WindowedApplication>
>
> Add the Example class
>
> package{
>     import flash.utils.IDataInput;
>     import flash.utils.IDataOutput;
>     import flash.utils.IExternalizable;
>
>     public class Example implements IExternalizable
>     {
>         public var name:String;
>         public function Example(name
>
>