1. The forums will be archived and moved to a read only mode in about 2 weeks (mid march).

Does an AsyncTask only accept PocketMine objects?

Discussion in 'Development' started by imYannic, Nov 19, 2016.

  1. imYannic

    imYannic Baby Zombie

    Messages:
    113
    Just wondering, I want to use an AsyncTask to execute long MySQL opertations, so I construct with the object which contains the MySQL class. But for some reason it never accepts objects which are not from PocketMine's source. When I construct with these objects, it prints the error "Serilization of 'Closure' is not allowed", the line is that line where I make the variable public ($this->plugin = $plugin). When I test it with Vector3 as a PocketMine object, it works and prints out no error. Is it not possible to use non-PocketMine objects for the AsyncTask?
     
  2. Thunder33345

    Thunder33345 Moderator Staff Member

    Messages:
    2,137
    GitHub:
    Thunder33345
    not very sure but from what i get via lookup it can happen when you try to serelize a anonymous function
     
  3. KnownUnown

    KnownUnown Spider Jockey Poggit Reviewer

    Messages:
    47
    GitHub:
    knownunown
    You shouldn't have a reference to the plugin as a field in the AsyncTask. Even if you don't access it from onRun, strange stuff could happen. Instead, try getting the plugin object from onCompletion from the Server object it supplies.
     
    HimbeersaftLP and imYannic like this.
  4. SOFe

    SOFe Administrator Staff Member PMMP Team Poggit Admin

    Messages:
    1,968
    GitHub:
    sof3
    Please look at pull requests #1 and $100 on pmmp repo. I have provided API methods to work around this limitation.

    You must not put anything with direct or indirect reference to the PocketMine Server class in your AsyncTask fields. AsyncTask is Threaded. It attempts to serialize everything you store in its class properties.
    Pull request #1 on pmmp repo provides a more convenient way to store values that you want to use in onCompletion(). But you must never use any objects from your plugin main class, except classes that are purely objects.

    As a matter of fact, I have written a library called libasynql https://GitHub.com/Poggit/libasynql. It provides a library to execute MySQL queries asynchronously. Even if you don't like the library, you can still refer to its code on how to use the new features in pull#1.
     
    imYannic and HimbeersaftLP like this.
  5. Jack Noordhuis

    Jack Noordhuis Zombie Pigman Poggit Reviewer

    Messages:
    618
    GitHub:
    JackNoordhuis
    It's best not to pass any objects over to an AsyncTask (makes everyone's life easier). If you absolutely have to pass an object the only 'safe' objects to move across threads in PHP are objects without any references to any other object.

    If you need to reference a player you'll need to save your targets name and use Server::getPlayerExact() to get your player instance (this can only be done while you have a reference to the Server object on the main thread). You have to do this as you can't pass the Player object across the main thread and worker thread('s) due to Player instances having references to objects on the main thread.
     
  6. SOFe

    SOFe Administrator Staff Member PMMP Team Poggit Admin

    Messages:
    1,968
    GitHub:
    sof3
    I forgot to mention that you should not pass the mysqli resource/object to AsyncTask. A resource is only valid in the thread it is constructed. In other threads, it is just a meaningless integer.

    Pull#1 adds the fetchLocal() method which can allow you to do something like this:

    However, this method should not be unnecessarily used to prevent memory leak. Always check if your stored properties are still valid before using them, or crashes may occur.

    It actually wouldn't result in the kind of memory leak that leads to memory exhaustion since your AsyncTask does not run indefinitely, but it would delay the garbage collection of some objects.
     
    imYannic and Jack Noordhuis like this.
  7. Jack Noordhuis

    Jack Noordhuis Zombie Pigman Poggit Reviewer

    Messages:
    618
    GitHub:
    JackNoordhuis
    You can already get the plugin instance from onCompletion? Server::getPluginManager()->getPlugin("PluginName");

    It's the onRun() method that you can't pass objects with references to/from as the code is executed in a worker thread.
     
  8. SOFe

    SOFe Administrator Staff Member PMMP Team Poggit Admin

    Messages:
    1,968
    GitHub:
    sof3
    Yes. Actually I wrote a bad example. It can be used to store other objects, such as the player object. It just saves the trouble of having to use long calls that you usually store in a class property instead.
     
  9. Jack Noordhuis

    Jack Noordhuis Zombie Pigman Poggit Reviewer

    Messages:
    618
    GitHub:
    JackNoordhuis
    So long as you serialize the object and store the result in a property and don't attempt to unserialize it or access it in the onRun() function you should be able to just unserialize the object in the onCompletion() function shouldn't you?

    Just curious as to how this is beneficial :p
     
  10. SOFe

    SOFe Administrator Staff Member PMMP Team Poggit Admin

    Messages:
    1,968
    GitHub:
    sof3
    In addition, the peekLocal() method (as of pull#100) is useful since you don't need to search for the player instance, but just check if it is still valid, whenever you make a progress update, if you want to regularly (like per second) update a player about the progress.

    If an object is serialized then unserialized, it is no longer the identical object (== but not ===). Consider a case where you have a MigrateDatabaseTask, where you update a player's popup every second telling him your progress. Because you don't want to send a popup whenever a file is copied, you repeat a task per second instead:

    Of course the MigrateProgress class may contain more data. I am just lazy to think of other data to put there.
    If you carelessly store the MigrateProgress class directly in a field in the AsyncTask, you still get a valid object with no errors, but your progress strangely stays at 0.0 forever because the MigrateProgress you get later is unserialize(serialize()), i.e. it is like a clone of the original one. Your post would mislead developers to have this bug.

    Actually, it is of course possible to keep the reference to the object in the main class like you wanted, but if there are multiple, and there is no unique identifier for this task, you have to make an explicit storage in your plugin just for AsyncTask.
    Pull#1 just helps you do this by putting the storage for all plugins in the same storage (ServerScheduler->objectStorage).
     
    imYannic and Jack Noordhuis like this.
  11. imYannic

    imYannic Baby Zombie

    Messages:
    113
    So the AsyncTask still goes and gets not executed on the main thread when I call my functions on onCompletion()?
     
  12. SOFe

    SOFe Administrator Staff Member PMMP Team Poggit Admin

    Messages:
    1,968
    GitHub:
    sof3
    What are you saying? You don't call onCompletion(). You can assume that PocketMine has a repeating task per tick that checks if an AsyncTask is completed, and calls onCompletion() once if it is completed.
     
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.