-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Added explicit AsyncTask->storeLocal(), removed AsyncTask->__construct() object storage #1322
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,7 +33,15 @@ | |
| * with no AsyncTask running. Therefore, an AsyncTask SHOULD NOT execute for more than a few seconds. For tasks that | ||
| * run for a long time or infinitely, start another {@link \pocketmine\Thread} instead. | ||
| * | ||
| * WARNING: Do not call PocketMine-MP API methods, or save objects (and arrays containing objects) from/on other Threads!! | ||
| * WARNING: Any non-Threaded objects WILL BE SERIALIZED when assigned to members of AsyncTasks or other Threaded object. | ||
| * If later accessed from said Threaded object, you will be operating on a COPY OF THE OBJECT, NOT THE ORIGINAL OBJECT. | ||
| * If you want to store non-serializable objects to access when the task completes, store them using | ||
| * {@link AsyncTask#storeLocal}. | ||
| * | ||
| * WARNING: As of pthreads v3.1.6, arrays are converted to Volatile objects when assigned as members of Threaded objects. | ||
| * Keep this in mind when using arrays stored as members of your AsyncTask. | ||
| * | ||
| * WARNING: Do not call PocketMine-MP API methods from other Threads!! | ||
| */ | ||
| abstract class AsyncTask extends Collectable{ | ||
|
|
||
|
|
@@ -51,29 +59,6 @@ abstract class AsyncTask extends Collectable{ | |
|
|
||
| private $crashed = false; | ||
|
|
||
| /** | ||
| * Constructs a new instance of AsyncTask. Subclasses don't need to call this constructor unless an argument is to be passed. ONLY construct this class from the main thread. | ||
| * <br> | ||
| * If an argument is passed into this constructor, it will be stored in a thread-local storage (in ServerScheduler), which MUST be retrieved through {@link #fetchLocal} when {@link #onCompletion} is called. | ||
| * Otherwise, a NOTICE level message will be raised and the reference will be removed after onCompletion exits. | ||
| * <br> | ||
| * If null or no argument is passed, do <em>not</em> call {@link #fetchLocal}, or an exception will be thrown. | ||
| * <br> | ||
| * WARNING: Use this method carefully. It might take a long time before an AsyncTask is completed. PocketMine will keep a strong reference to objects passed in this method. | ||
| * This may result in a light memory leak. Usually this does not cause memory failure, but be aware that the object may be no longer usable when the AsyncTask completes. | ||
| * (E.g. a {@link \pocketmine\Level} object is no longer usable because it is unloaded while the AsyncTask is executing, or even a plugin might be unloaded) | ||
| * Since PocketMine keeps a strong reference, the objects are still valid, but the implementation is responsible for checking whether these objects are still usable. | ||
| * | ||
| * @param mixed $complexData the data to store, pass null to store nothing. Scalar types can be safely stored in class properties directly instead of using this thread-local storage. | ||
| */ | ||
| public function __construct($complexData = null){ | ||
| if($complexData === null){ | ||
| return; | ||
| } | ||
|
|
||
| Server::getInstance()->getScheduler()->storeLocalComplex($this, $complexData); | ||
| } | ||
|
|
||
| public function run(){ | ||
| $this->result = null; | ||
|
|
||
|
|
@@ -216,14 +201,44 @@ public function onProgressUpdate(Server $server, $progress){ | |
| } | ||
|
|
||
| /** | ||
| * Call this method from {@link AsyncTask#onCompletion} to fetch the data stored in the constructor, if any, and | ||
| * clears it from the storage. | ||
| * Saves mixed data in ServerScheduler's object storage on the main thread. You may use this to retain references to | ||
| * objects or arrays which you need to access in {@link AsyncTask#onCompletion} which cannot be stored as a | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. onCompletion() or onProgressUpdate().
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's best not to introduce more confusion over fetch vs peek. This documents behaviour and the most common use, it's up to developers to decide how to use it. |
||
| * property of your task. | ||
| * | ||
| * Scalar types can be stored directly in class properties instead of using this storage. | ||
| * | ||
| * Objects stored in this storage MUST be retrieved through {@link #fetchLocal} when {@link #onCompletion} is called. | ||
| * Otherwise, a NOTICE level message will be raised and the reference will be removed after onCompletion exits. | ||
| * | ||
| * Do not call this method from {@link AsyncTask#onProgressUpdate}, because this method deletes the data and cannot | ||
| * be used in the next {@link AsyncTask#onProgressUpdate} call or from {@link AsyncTask#onCompletion}. Use | ||
| * {@link AsyncTask#peekLocal} instead. | ||
| * WARNING: Use this method carefully. It might take a long time before an AsyncTask is completed. PocketMine will | ||
| * keep a strong reference to objects passed in this method. This may result in a light memory leak. Usually this | ||
| * does not cause memory failure, but be aware that the object may be no longer usable when the AsyncTask completes. | ||
| * (E.g. a {@link \pocketmine\Level} object is no longer usable because it is unloaded while the AsyncTask is | ||
| * executing, or even a plugin might be unloaded). Since PocketMine keeps a strong reference, the objects are still | ||
| * valid, but the implementation is responsible for checking whether these objects are still usable. | ||
| * | ||
| * @param Server $server default null | ||
| * WARNING: THIS METHOD SHOULD ONLY BE CALLED FROM THE MAIN THREAD! | ||
| * | ||
| * @param mixed $complexData the data to store | ||
| */ | ||
| protected function storeLocal($complexData){ | ||
| Server::getInstance()->getScheduler()->storeLocalComplex($this, $complexData); | ||
| } | ||
|
|
||
| /** | ||
| * Returns mixed data stored in the ServerScheduler's object store and clears it. Call this method from | ||
| * {@link AsyncTask#onCompletion} to fetch the data stored in the object store, if any. | ||
| * | ||
| * If no data was stored in the local store, or if the data was already retrieved by a previous call to fetchLocal, | ||
| * do NOT call this method, or an exception will be thrown. | ||
| * | ||
| * Do not call this method from {@link AsyncTask#onProgressUpdate}, because this method deletes stored data, which | ||
| * means that you will not be able to retrieve it again afterwards. Use {@link AsyncTask#peekLocal} instead to | ||
| * retrieve stored data without removing it from the store. | ||
| * | ||
| * WARNING: THIS METHOD SHOULD ONLY BE CALLED FROM THE MAIN THREAD! | ||
| * | ||
| * @param Server|null $server | ||
| * | ||
| * @return mixed | ||
| * | ||
|
|
@@ -232,20 +247,26 @@ public function onProgressUpdate(Server $server, $progress){ | |
| protected function fetchLocal(Server $server = null){ | ||
| if($server === null){ | ||
| $server = Server::getInstance(); | ||
| assert($server !== null, "Call this method only from the main thread!"); | ||
| if($server === null){ | ||
| throw new \BadMethodCallException("Stored objects can only be retrieved from the main thread"); | ||
| } | ||
| } | ||
|
|
||
| return $server->getScheduler()->fetchLocalComplex($this); | ||
| } | ||
|
|
||
| /** | ||
| * Call this method from {@link AsyncTask#onProgressUpdate} to fetch the data stored in the constructor. | ||
| * Returns mixed data stored in the ServerScheduler's object store **without clearing** it. Call this method from | ||
| * {@link AsyncTask#onProgressUpdate} to fetch the data stored if you need to be able to access the data later on, | ||
| * such as in another progress update. | ||
| * | ||
| * Use {@link AsyncTask#peekLocal} instead from {@link AsyncTask#onCompletion}, because this method does not delete | ||
| * the data, and not clearing the data will result in a warning for memory leak after {@link AsyncTask#onCompletion} | ||
| * finished executing. | ||
| * | ||
| * @param Server|null $server default null | ||
| * WARNING: THIS METHOD SHOULD ONLY BE CALLED FROM THE MAIN THREAD! | ||
| * | ||
| * @param Server|null $server | ||
| * | ||
| * @return mixed | ||
| * | ||
|
|
@@ -254,7 +275,9 @@ protected function fetchLocal(Server $server = null){ | |
| protected function peekLocal(Server $server = null){ | ||
| if($server === null){ | ||
| $server = Server::getInstance(); | ||
| assert($server !== null, "Call this method only from the main thread!"); | ||
| if($server === null){ | ||
| throw new \BadMethodCallException("Stored objects can only be peeked from the main thread"); | ||
| } | ||
| } | ||
|
|
||
| return $server->getScheduler()->peekLocalComplex($this); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? Does "other Threads" include the main thread? Because I call
NBT::readCompressed()andItem::nbtDeserialize()in one of my plugins asynchronously.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A lot of things are not initialized on workers that are initialized on the main thread, which may cause unexpected behaviour. For example, because the ItemFactory is not initialized, calling nbtDeserialize() on a worker will give you a generic
Itemobject no matter what item was stored.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh noes... I messed up in many places.