Instance from thread doesn't get updated

Post here your questions about the Flash / Flex / Air API for SFS2X

Moderators: Lapo, Bax

MrUseL3tter
Posts: 2
Joined: 14 Feb 2014, 02:55

Instance from thread doesn't get updated

Postby MrUseL3tter » 22 Feb 2014, 11:15

Currently, our app has a zone extension and extensions for different Rooms.
In our Zone extension, we create a ScheduledFuture thread that executes every second and loops through a collection of a model class with an integer value that we want to keep track of:

RawSchedule.java - we need to keep track of secondsRemaining here

Code: Select all

 
package com.noobs2d.jumbocinema.youtube;

public class RawSchedule {

    private int duration;
    private boolean playing;
    private int secondsRemaining;
    private String watchDate;
    private boolean removable;

    public RawSchedule(int duration, boolean playing, int secondsRemaining, String watchDate) {
   this.duration = duration;
   this.playing = playing;
   this.secondsRemaining = secondsRemaining;
   this.watchDate = watchDate;
    }

    public void decrementSecondsRemaining() {
   secondsRemaining--;
    }

    public int getDuration() {
   return duration;
    }

    public int getSecondsRemaining() {
   return secondsRemaining;
    }

    public String getWatchDate() {
   return watchDate;
    }

    public boolean isPlaying() {
   return playing;
    }

    public boolean isRemovable() {
   return removable;
    }

    public boolean isSecondsRemainingNegative() {
   return secondsRemaining < 0;
    }

    public void play() {
   playing = true;
    }

    public void setRemovable(boolean removable) {
   this.removable = removable;
    }

    /**
     * Makes the secondsRemaining become the duration. This should only be called when the schedule starts playing.
     */
    public void switchSecondsRemaining() {
   secondsRemaining = duration;
    }

}


After updating, we send it back to all users through this line (see ScheduleTimer.java below):

Code: Select all

parentExtension.send(schedules.get(i).getWatchDate(), returnData, parentExtension.getParentZone().getUserManager().getAllUsers());


I've tried tracing the value of that model's instance variable and it updates fine, but once we send it back to all the users in the zone, the updated value of the instance in the thread doesn't get passed.
For example, if the instance of RawSchedule's value of secondsRemaining is 340 and a user logs in, he gets the 340 every second, but it stays like that, while in the server, the trace continues to change (decrement, as planned).

The way we made the extensions is through a single project only; we just change the way which main extension class to use (i.e., we have MainZoneExtension that we use for the zone and Main that we use for the rooms, which is in a single jar file. Since we need to get the instance of the timer through casting getParentExtension() to MainZonExtension to get a reference to our timer thread, we had to share the jar file in __lib__ as instructed here.

The following are classes that is related to this problem.

ScheduleTimer.java

Code: Select all

package com.noobs2d.jumbocinema.youtube;

import java.util.ArrayList;

public class ScheduleTimer {

    private class TaskRunner implements Runnable {

   @Override
   public void run() {
       checkRemoveableItems();
       sendTime();
   }

   private void checkRemoveableItems() {
       try {
      for (int i = 0; i < schedules.size(); i++)
          if (schedules.get(i).isRemovable())
         schedules.remove(i);
       } catch (Exception e) {
      parentExtension.trace("Error removing instance from collection: " + e.getMessage());
       }
   }
    }

    private SFSExtension parentExtension;
    private ArrayList<RawSchedule> schedules;

    public ScheduleTimer(SFSExtension parentExtension) {
   this.parentExtension = parentExtension;
   schedules = new ArrayList<RawSchedule>();
   SmartFoxServer smartFoxServer = SmartFoxServer.getInstance();
   smartFoxServer.getTaskScheduler().scheduleAtFixedRate(new TaskRunner(), 0, 1, TimeUnit.SECONDS);
    }

    public void addSchedule(RawSchedule scheduleToAdd) {
   try {
       boolean notInCollectionYet = true;
       for (int i = 0; i < schedules.size(); i++)
      if (schedules.get(i).getWatchDate() == scheduleToAdd.getWatchDate()) {
          notInCollectionYet = false;
          i = schedules.size(); //break this loop
      }

       parentExtension.trace("Adding " + scheduleToAdd.getWatchDate() + " to the thread...");
       if (notInCollectionYet) {
      schedules.add(scheduleToAdd);
      parentExtension.trace("Added " + scheduleToAdd.getWatchDate() + " to the thread!");

       }
   } catch (Exception e) {
       parentExtension.trace("Adding schedule on timer error: " + e.getMessage());
   }
    }

    private void sendTime() {
   try {
       for (int i = 0; i < schedules.size(); i++) {
      schedules.get(i).decrementSecondsRemaining();

      boolean swapScheduleTimers = false;
      boolean removeItem = false;
      if (schedules.get(i).isSecondsRemainingNegative())
          if (schedules.get(i).isPlaying())
         removeItem = true;
          else {
         schedules.get(i).play();
         swapScheduleTimers = true;
          }
      ISFSObject returnData = SFSObject.newInstance();
      returnData.putInt("REMAINING", (int) (Math.random() * 10000));
      returnData.putBool("PLAYING", schedules.get(i).isPlaying());

      parentExtension.send(schedules.get(i).getWatchDate(), returnData, parentExtension.getParentZone().getUserManager().getAllUsers());

      if (swapScheduleTimers)
          schedules.get(i).switchSecondsRemaining();

      schedules.get(i).setRemovable(removeItem);
       }
   } catch (Exception e) {
       parentExtension.trace("Timer thread error: " + e.getMessage());
   }
    }

}



MainZoneExtension.java

Code: Select all

package com.noobs2d.jumbocinema;

import com.noobs2d.jumbocinema.youtube.ScheduleTimer;
import com.smartfoxserver.openspace.OpenSpaceExtension;

public class MainZoneExtension extends OpenSpaceExtension {

    private ScheduleTimer timer;

    @Override
    public void _init() {
   super._init();
   trace("ZONE EXTENSION LOADED!");
   timer = new ScheduleTimer(this);
    }

    public ScheduleTimer getTimer() {
   return timer;
    }

}


Are we doing a bad architecture of the extensions? It seems to me like the instances inside the thread are getting maintained. I even tried returning a random value every thread execution but it's still the same; I get the same value every extension response in the client but the trace in the server side says a different and updated value.

I hope you guys can help us with this. Thanks a lot!
User avatar
Lapo
Site Admin
Posts: 23009
Joined: 21 Mar 2005, 09:50
Location: Italy

Re: Instance from thread doesn't get updated

Postby Lapo » 24 Feb 2014, 09:40

Hi,
you have posted quite a lot of code and it's pretty difficult to tell what might be going on.

I've tried tracing the value of that model's instance variable and it updates fine, but once we send it back to all the users in the zone, the updated value of the instance in the thread doesn't get passed.

Do you mean the value is not received by the client or it is received but it not the expected one?

At a quick glance I've noticed several issues, especially with thread safety,
You are using a non thread safe collection (ArrayList) and this code won't do what you think:

Code: Select all

boolean notInCollectionYet = true;
       for (int i = 0; i < schedules.size(); i++)
      if (schedules.get(i).getWatchDate() == scheduleToAdd.getWatchDate()) {
          notInCollectionYet = false;
          i = schedules.size(); //break this loop
      }


Two threads running the same code will very easily add the item twice because there's no mutual exclusion. You will need to synchronize the code here.

Code reviews are beyond the scope of this support forum, I would suggest to fix these issues first. Then try to narrow down where the problem is by debugging the code some more and give us a more precise description of where the issue is found in your code.

Thanks
Lapo
--
gotoAndPlay()
...addicted to flash games
MrUseL3tter
Posts: 2
Joined: 14 Feb 2014, 02:55

Re: Instance from thread doesn't get updated

Postby MrUseL3tter » 24 Feb 2014, 15:06

Thanks for the reply! I've found the stupid mistake that causes the problem now. Anyway, thanks for warning me about the thread-safety of my code. I understand now that it's not really a good one. Will change it immediately. More powers! :D

Return to “SFS2X ActionScript 3 API”

Who is online

Users browsing this forum: No registered users and 15 guests