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

Reply via email to