FPLBase
An open source project by FPL.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Groups Pages
async_loader.h
Go to the documentation of this file.
1 // Copyright 2014 Google Inc. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef FPLBASE_ASYNC_LOADER_H
16 #define FPLBASE_ASYNC_LOADER_H
17 
18 #include <stdint.h>
19 #include <deque>
20 #include <functional>
21 #include <string>
22 #include <vector>
23 
24 #include "fplbase/config.h" // Must come first.
25 #include "fplbase/asset.h"
26 
27 #ifdef FPLBASE_BACKEND_STDLIB
28 #include <mutex>
29 #include <thread>
30 #include <condition_variable>
31 #endif
32 
33 namespace fplbase {
34 
35 /// @file
36 /// @addtogroup fplbase_async_loader
37 /// @{
38 
39 typedef void *Thread;
40 typedef void *Mutex;
41 typedef void *Semaphore;
42 
43 class AsyncLoader;
44 
45 /// @class AsyncResource
46 /// @brief Any resource that can be loaded asynchronously should inherit from
47 /// this.
48 class AsyncAsset : public Asset {
49  public:
50  /// @brief A function pointer to an asset loaded callback function.
51  typedef std::function<void()> AssetFinalizedCallback;
52 
53  /// @brief Default constructor for an empty AsyncAsset.
54  AsyncAsset() : data_(nullptr) {}
55 
56  /// @brief Construct an AsyncAsset with a given file name.
57  /// @param[in] filename A C-string corresponding to the name of the asset
58  /// file.
59  explicit AsyncAsset(const char *filename)
60  : filename_(filename), data_(nullptr), finalize_callbacks_(0) {}
61 
62  /// @brief AsyncAsset destructor.
63  virtual ~AsyncAsset() {}
64 
65  /// @brief Override with the actual loading behavior.
66  ///
67  /// Load should perform the actual loading of filename_, and store the
68  /// result in data_, or nullptr upon failure. It is called on the loader
69  /// thread, so should not access any program state outside of this object.
70  /// Since there will be only one loader thread, any libraries called by Load
71  /// need not be MT-safe as long as they're not also called by the main thread.
72  virtual void Load() = 0;
73 
74  /// @brief Override with converting the data into the resource.
75  ///
76  /// This should implement the behavior of turning data_ into the actual
77  /// desired resource. Called on the main thread only.
78  /// Should check if data_ is null.
79  virtual bool Finalize() = 0;
80 
81  /// @brief Whether this object loaded and finalized correctly. Call after
82  /// Finalize has been called (by AssetManager::TryFinalize).
83  virtual bool IsValid() = 0;
84 
85  /// @brief Performs a synchronous load by calling Load & Finalize.
86  ///
87  /// Not used by the loader thread, should be called on the main thread.
88  /// @return Returns false on failure.
89  bool LoadNow() {
90  Load();
91  bool ok = data_ != nullptr;
92  // Call this even if data_ is null, to enforce Finalize() checking for it.
93  return Finalize() && ok;
94  }
95 
96  /// @brief Sets the filename that should be loaded.
97  ///
98  /// Set the the filename in situations where it can't be initialized in
99  /// the constructor. Must be called before AsyncLoader::QueueJob().
100  ///
101  /// @param filename The name of the file to load.
102  void set_filename(const std::string &filename) { filename_ = filename; }
103 
104  /// @brief The name of the file associated with the resource.
105  ///
106  /// @return Returns the filename.
107  const std::string &filename() const { return filename_; }
108 
109  /// @brief Adds a callback to be called when the asset is finalized.
110  ///
111  /// Add a callback so logic can be executed when an asset is done loading.
112  ///
113  /// @param callback The function to be called.
115  finalize_callbacks_.push_back(callback);
116  }
117 
118  protected:
119  /// @brief Calls app callbacks when an asset is ready to be used.
120  ///
121  /// This should be called by descendants as soon as they are finalized.
123  for (auto it = finalize_callbacks_.begin();
124  it != finalize_callbacks_.end(); ++it) {
125  (*it)();
126  }
127  finalize_callbacks_.clear();
128  }
129 
130  /// @brief The resource file name.
131  std::string filename_;
132  /// @brief The resource data.
133  const uint8_t *data_;
134 
135  std::vector<AssetFinalizedCallback> finalize_callbacks_;
136 
137  friend class AsyncLoader;
138 };
139 
140 /// @class AsyncLoader
141 /// @brief Handles loading AsyncAsset objects.
142 class AsyncLoader {
143  public:
144  AsyncLoader();
145  ~AsyncLoader();
146 
147  /// @brief Queues AsyncResources to be loaded by StartLoading.
148  ///
149  /// Call this any number of times before StartLoading.
150  ///
151  /// @param res The resource to queue for loading.
152  void QueueJob(AsyncAsset *res);
153 
154  /// @brief Launches the loading thread for the previously queued jobs.
155  void StartLoading();
156 
157  /// @brief Pause the loading thread for previously queued jobs.
158  ///
159  /// Blocks until only the current job is finished loading. You can resume
160  /// loading assets by calling StartLoading().
161  void PauseLoading();
162 
163  /// @brief Ends the loading thread when all jobs are done.
164  ///
165  /// Cleans-up the background loading thread once all jobs have been completed.
166  /// You can restart with StartLoading() if you like.
168 
169  /// @brief Call to Finalize any resources that have finished loading.
170  ///
171  /// Call this once per frame after StartLoading. Will call Finalize on any
172  /// resources that have finished loading. One it returns true, that means
173  /// the queue is empty, all resources have been processed, and the loading
174  /// thread has terminated.
175  ///
176  /// @return Returns true once the queue is empty.
177  bool TryFinalize();
178 
179  /// @brief Shuts down the loader after completing all pending loads.
180  void Stop();
181 
182  private:
183 #ifdef FPLBASE_BACKEND_SDL
184  void Lock(const std::function<void()> &body);
185  template <typename T>
186  T LockReturn(const std::function<T()> &body) {
187  T ret;
188  Lock([&ret, &body]() { ret = body(); });
189  return ret;
190  }
191 #endif
192 
193  void LoaderWorker();
194  static int LoaderThread(void *user_data);
195 
196  std::deque<AsyncAsset *> queue_, done_;
197 #ifdef FPLBASE_BACKEND_SDL
198  // Keep handle to the worker thread around so that we can wait for it to
199  // finish before destroying the class.
200  Thread worker_thread_;
201 
202  // This lock protects ALL state in this class, i.e. the two vectors.
203  Mutex mutex_;
204 
205  // Kick-off the worker thread when a new job arrives.
206  Semaphore job_semaphore_;
207 #elif defined(FPLBASE_BACKEND_STDLIB)
208  std::thread worker_thread_;
209  std::mutex mutex_;
210  std::condition_variable job_cv_;
211 #else
212 #error Need to define FPLBASE_BACKEND_XXX
213 #endif
214 };
215 
216 /// @}
217 } // namespace fplbase
218 
219 #endif // FPLBASE_ASYNC_LOADER_H
void AddFinalizeCallback(AssetFinalizedCallback callback)
Adds a callback to be called when the asset is finalized.
Definition: async_loader.h:114
void Stop()
Shuts down the loader after completing all pending loads.
virtual bool Finalize()=0
Override with converting the data into the resource.
virtual bool IsValid()=0
Whether this object loaded and finalized correctly. Call after Finalize has been called (by AssetMana...
void StopLoadingWhenComplete()
Ends the loading thread when all jobs are done.
const std::string & filename() const
The name of the file associated with the resource.
Definition: async_loader.h:107
std::function< void()> AssetFinalizedCallback
A function pointer to an asset loaded callback function.
Definition: async_loader.h:51
Base class of all assets that may be managed by Assetmanager.
Definition: asset.h:26
AsyncAsset(const char *filename)
Construct an AsyncAsset with a given file name.
Definition: async_loader.h:59
void CallFinalizeCallback()
Calls app callbacks when an asset is ready to be used.
Definition: async_loader.h:122
void set_filename(const std::string &filename)
Sets the filename that should be loaded.
Definition: async_loader.h:102
const uint8_t * data_
The resource data.
Definition: async_loader.h:133
Handles loading AsyncAsset objects.
Definition: async_loader.h:142
std::string filename_
The resource file name.
Definition: async_loader.h:131
virtual ~AsyncAsset()
AsyncAsset destructor.
Definition: async_loader.h:63
virtual void Load()=0
Override with the actual loading behavior.
AsyncAsset()
Default constructor for an empty AsyncAsset.
Definition: async_loader.h:54
bool TryFinalize()
Call to Finalize any resources that have finished loading.
void QueueJob(AsyncAsset *res)
Queues AsyncResources to be loaded by StartLoading.
void StartLoading()
Launches the loading thread for the previously queued jobs.
bool LoadNow()
Performs a synchronous load by calling Load & Finalize.
Definition: async_loader.h:89
Definition: async_loader.h:48
void PauseLoading()
Pause the loading thread for previously queued jobs.