added iOS source code
[wl-app.git] / iOS / Pods / Realm / include / sync / sync_session.hpp
1 ////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright 2016 Realm Inc.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 ////////////////////////////////////////////////////////////////////////////
18
19 #ifndef REALM_OS_SYNC_SESSION_HPP
20 #define REALM_OS_SYNC_SESSION_HPP
21
22 #include "feature_checks.hpp"
23 #include "sync/sync_config.hpp"
24
25 #include <realm/util/optional.hpp>
26 #include <realm/version_id.hpp>
27
28 #include <mutex>
29 #include <unordered_map>
30
31 namespace realm {
32
33 class SyncManager;
34 class SyncUser;
35
36 namespace _impl {
37 class RealmCoordinator;
38 struct SyncClient;
39
40 namespace sync_session_states {
41 struct WaitingForAccessToken;
42 struct Active;
43 struct Dying;
44 struct Inactive;
45 }
46 }
47
48 namespace sync {
49 class Session;
50 }
51
52 using SyncSessionTransactCallback = void(VersionID old_version, VersionID new_version);
53 using SyncProgressNotifierCallback = void(uint64_t transferred_bytes, uint64_t transferrable_bytes);
54
55 class SyncSession : public std::enable_shared_from_this<SyncSession> {
56 public:
57     enum class PublicState {
58         WaitingForAccessToken,
59         Active,
60         Dying,
61         Inactive,
62
63         // FIXME: This state no longer exists. This should be removed.
64         Error,
65     };
66     PublicState state() const;
67
68     // FIXME: The error state no longer exists. This should be removed.
69     bool is_in_error_state() const { return false; }
70
71     // The on-disk path of the Realm file backing the Realm this `SyncSession` represents.
72     std::string const& path() const { return m_realm_path; }
73
74     // Register a callback that will be called when all pending uploads have completed.
75     // The callback is run asynchronously, and upon whatever thread the underlying sync client
76     // chooses to run it on. The method returns immediately with true if the callback was
77     // successfully registered, false otherwise. If the method returns false the callback will
78     // never be run.
79     // This method will return true if the completion handler was registered, either immediately
80     // or placed in a queue. If it returns true the completion handler will always be called
81     // at least once, except in the case where a logged-out session is never logged back in.
82     bool wait_for_upload_completion(std::function<void(std::error_code)> callback);
83
84     // Register a callback that will be called when all pending downloads have been completed.
85     // Works the same way as `wait_for_upload_completion()`.
86     bool wait_for_download_completion(std::function<void(std::error_code)> callback);
87
88     enum class NotifierType {
89         upload, download
90     };
91     // Register a notifier that updates the app regarding progress.
92     //
93     // If `m_current_progress` is populated when this method is called, the notifier
94     // will be called synchronously, to provide the caller with an initial assessment
95     // of the state of synchronization. Otherwise, the progress notifier will be
96     // registered, and only called once sync has begun providing progress data.
97     //
98     // If `is_streaming` is true, then the notifier will be called forever, and will
99     // always contain the most up-to-date number of downloadable or uploadable bytes.
100     // Otherwise, the number of downloaded or uploaded bytes will always be reported
101     // relative to the number of downloadable or uploadable bytes at the point in time
102     // when the notifier was registered.
103     //
104     // An integer representing a token is returned. This token can be used to manually
105     // unregister the notifier. If the integer is 0, the notifier was not registered.
106     //
107     // Note that bindings should dispatch the callback onto a separate thread or queue
108     // in order to avoid blocking the sync client.
109     uint64_t register_progress_notifier(std::function<SyncProgressNotifierCallback>, NotifierType, bool is_streaming);
110
111     // Unregister a previously registered notifier. If the token is invalid,
112     // this method does nothing.
113     void unregister_progress_notifier(uint64_t);
114
115     // If possible, take the session and do anything necessary to make it `Active`.
116     // Specifically:
117     // If the sync session is currently `Dying`, ask it to stay alive instead.
118     // If the sync session is currently `WaitingForAccessToken`, cancel any deferred close.
119     // If the sync session is currently `Inactive`, recreate it.
120     // Otherwise, a no-op.
121     void revive_if_needed();
122
123     // Perform any actions needed in response to regaining network connectivity.
124     // Specifically:
125     // If the sync session is currently `WaitingForAccessToken`, make the binding ask the auth server for a token.
126     // Otherwise, a no-op.
127     void handle_reconnect();
128
129     // Give the `SyncSession` a new, valid token, and ask it to refresh the underlying session.
130     // If the session can't accept a new token, this method does nothing.
131     // Note that, if this is the first time the session will be given a token, `server_url` must
132     // be set.
133     void refresh_access_token(std::string access_token, util::Optional<std::string> server_url);
134
135     // FIXME: we need an API to allow the binding to tell sync that the access token fetch failed
136     // or was cancelled, and cannot be retried.
137
138     // Set the multiplex identifier used for this session. Sessions with different identifiers are
139     // never multiplexed into a single connection, even if they are connecting to the same host.
140     // The value of the token is otherwise treated as an opaque token.
141     //
142     // Has no effect if session multiplexing is not enabled (see SyncManager::enable_session_multiplexing())
143     // or if called after the Sync session is created. In particular, changing the multiplex identity will
144     // not make the session reconnect.
145     void set_multiplex_identifier(std::string multiplex_identity);
146
147     // Inform the sync session that it should close.
148     void close();
149
150     // Inform the sync session that it should log out.
151     void log_out();
152
153     // Override the address and port of the server that this `SyncSession` is connected to. If the
154     // session is already connected, it will disconnect and then reconnect to the specified address.
155     // If it's not already connected, future connection attempts will be to the specified address.
156     //
157     // NOTE: This is intended for use only in very specific circumstances. Please check with the
158     // object store team before using it.
159     void override_server(std::string address, int port);
160
161     // An object representing the user who owns the Realm this `SyncSession` represents.
162     std::shared_ptr<SyncUser> user() const
163     {
164         return m_config.user;
165     }
166
167     // A copy of the configuration object describing the Realm this `SyncSession` represents.
168     const SyncConfig& config() const
169     {
170         return m_config;
171     }
172
173     // If the `SyncSession` has been configured, the full remote URL of the Realm
174     // this `SyncSession` represents.
175     util::Optional<std::string> full_realm_url() const
176     {
177         return m_server_url;
178     }
179
180     // Create an external reference to this session. The sync session attempts to remain active
181     // as long as an external reference to the session exists.
182     std::shared_ptr<SyncSession> external_reference();
183
184     // Return an existing external reference to this session, if one exists. Otherwise, returns `nullptr`.
185     std::shared_ptr<SyncSession> existing_external_reference();
186
187     // Expose some internal functionality to other parts of the ObjectStore
188     // without making it public to everyone
189     class Internal {
190         friend class _impl::RealmCoordinator;
191
192         static void set_sync_transact_callback(SyncSession& session,
193                                                std::function<SyncSessionTransactCallback> callback)
194         {
195             session.set_sync_transact_callback(std::move(callback));
196         }
197
198         static void nonsync_transact_notify(SyncSession& session, VersionID::version_type version)
199         {
200             session.nonsync_transact_notify(version);
201         }
202     };
203
204     // Expose some internal functionality to testing code.
205     struct OnlyForTesting {
206         static void handle_error(SyncSession& session, SyncError error)
207         {
208             session.handle_error(std::move(error));
209         }
210
211         static void handle_progress_update(SyncSession& session, uint64_t downloaded, uint64_t downloadable,
212                                            uint64_t uploaded, uint64_t uploadable, bool is_fresh=true) {
213             session.handle_progress_update(downloaded, downloadable, uploaded, uploadable, is_fresh);
214         }
215
216         static bool has_stale_progress(SyncSession& session)
217         {
218             return session.m_current_progress != none && !session.m_latest_progress_data_is_fresh;
219         }
220
221         static bool has_fresh_progress(SyncSession& session)
222         {
223             return session.m_latest_progress_data_is_fresh;
224         }
225     };
226
227 private:
228     using std::enable_shared_from_this<SyncSession>::shared_from_this;
229
230     struct State;
231     friend struct _impl::sync_session_states::WaitingForAccessToken;
232     friend struct _impl::sync_session_states::Active;
233     friend struct _impl::sync_session_states::Dying;
234     friend struct _impl::sync_session_states::Inactive;
235
236     friend class realm::SyncManager;
237     // Called by SyncManager {
238     static std::shared_ptr<SyncSession> create(_impl::SyncClient& client, std::string realm_path, SyncConfig config)
239     {
240         struct MakeSharedEnabler : public SyncSession {
241             MakeSharedEnabler(_impl::SyncClient& client, std::string realm_path, SyncConfig config)
242             : SyncSession(client, std::move(realm_path), std::move(config))
243             {}
244         };
245         return std::make_shared<MakeSharedEnabler>(client, std::move(realm_path), std::move(config));
246     }
247     // }
248
249     SyncSession(_impl::SyncClient&, std::string realm_path, SyncConfig);
250
251     void handle_error(SyncError);
252     void cancel_pending_waits();
253     enum class ShouldBackup { yes, no };
254     void update_error_and_mark_file_for_deletion(SyncError&, ShouldBackup);
255     static std::string get_recovery_file_path();
256     void handle_progress_update(uint64_t, uint64_t, uint64_t, uint64_t, bool);
257
258     void set_sync_transact_callback(std::function<SyncSessionTransactCallback>);
259     void nonsync_transact_notify(VersionID::version_type);
260
261     void advance_state(std::unique_lock<std::mutex>& lock, const State&);
262
263     void create_sync_session();
264     void unregister(std::unique_lock<std::mutex>& lock);
265     void did_drop_external_reference();
266
267     std::function<SyncSessionTransactCallback> m_sync_transact_callback;
268
269     // How many bytes are uploadable or downloadable.
270     struct Progress {
271         uint64_t uploadable;
272         uint64_t downloadable;
273         uint64_t uploaded;
274         uint64_t downloaded;
275     };
276
277     // A PODS encapsulating some information for progress notifier callbacks a binding
278     // can register upon this session.
279     struct NotifierPackage {
280         std::function<SyncProgressNotifierCallback> notifier;
281         bool is_streaming;
282         NotifierType direction;
283         util::Optional<uint64_t> captured_transferrable;
284
285         void update(const Progress&, bool);
286         std::function<void()> create_invocation(const Progress&, bool&) const;
287     };
288
289     // A counter used as a token to identify progress notifier callbacks registered on this session.
290     uint64_t m_progress_notifier_token = 1;
291     bool m_latest_progress_data_is_fresh;
292
293     // Will be `none` until we've received the initial notification from sync.  Note that this
294     // happens only once ever during the lifetime of a given `SyncSession`, since these values are
295     // expected to semi-monotonically increase, and a lower-bounds estimate is still useful in the
296     // event more up-to-date information isn't yet available.  FIXME: If we support transparent
297     // client reset in the future, we might need to reset the progress state variables if the Realm
298     // is rolled back.
299     util::Optional<Progress> m_current_progress;
300
301     std::unordered_map<uint64_t, NotifierPackage> m_notifiers;
302
303     mutable std::mutex m_state_mutex;
304     mutable std::mutex m_progress_notifier_mutex;
305
306     const State* m_state = nullptr;
307     size_t m_death_count = 0;
308
309     SyncConfig m_config;
310
311     std::string m_realm_path;
312     _impl::SyncClient& m_client;
313
314     // For storing wait-for-completion requests if the session isn't yet ready to handle them.
315     struct CompletionWaitPackage {
316         void(sync::Session::*waiter)(std::function<void(std::error_code)>);
317         std::function<void(std::error_code)> callback;
318     };
319     std::vector<CompletionWaitPackage> m_completion_wait_packages;
320
321     struct ServerOverride {
322         std::string address;
323         int port;
324     };
325     util::Optional<ServerOverride> m_server_override;
326
327     // The underlying `Session` object that is owned and managed by this `SyncSession`.
328     // The session is first created when the `SyncSession` is moved out of its initial `inactive` state.
329     // The session might be destroyed if the `SyncSession` becomes inactive again (for example, if the
330     // user owning the session logs out). It might be created anew if the session is revived (if a
331     // logged-out user logs back in, the object store sync code will revive their sessions).
332     std::unique_ptr<sync::Session> m_session;
333
334     // Whether or not the session object in `m_session` has been `bind()`ed before.
335     // This determines how the `SyncSession` behaves when refreshing tokens.
336     bool m_session_has_been_bound;
337
338     util::Optional<int_fast64_t> m_deferred_commit_notification;
339     bool m_deferred_close = false;
340
341     // The fully-resolved URL of this Realm, including the server and the path.
342     util::Optional<std::string> m_server_url;
343
344     std::string m_multiplex_identity;
345
346     class ExternalReference;
347     std::weak_ptr<ExternalReference> m_external_reference;
348 };
349
350 }
351
352 #endif // REALM_OS_SYNC_SESSION_HPP