1 ////////////////////////////////////////////////////////////////////////////
3 // Copyright 2016 Realm Inc.
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
9 // http://www.apache.org/licenses/LICENSE-2.0
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.
17 ////////////////////////////////////////////////////////////////////////////
19 #ifndef REALM_OS_SYNC_SESSION_HPP
20 #define REALM_OS_SYNC_SESSION_HPP
22 #include "feature_checks.hpp"
23 #include "sync/sync_config.hpp"
25 #include <realm/util/optional.hpp>
26 #include <realm/version_id.hpp>
29 #include <unordered_map>
37 class RealmCoordinator;
40 namespace sync_session_states {
41 struct WaitingForAccessToken;
52 using SyncSessionTransactCallback = void(VersionID old_version, VersionID new_version);
53 using SyncProgressNotifierCallback = void(uint64_t transferred_bytes, uint64_t transferrable_bytes);
55 class SyncSession : public std::enable_shared_from_this<SyncSession> {
57 enum class PublicState {
58 WaitingForAccessToken,
63 // FIXME: This state no longer exists. This should be removed.
66 PublicState state() const;
68 // FIXME: The error state no longer exists. This should be removed.
69 bool is_in_error_state() const { return false; }
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; }
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
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);
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);
88 enum class NotifierType {
91 // Register a notifier that updates the app regarding progress.
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.
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.
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.
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);
111 // Unregister a previously registered notifier. If the token is invalid,
112 // this method does nothing.
113 void unregister_progress_notifier(uint64_t);
115 // If possible, take the session and do anything necessary to make it `Active`.
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();
123 // Perform any actions needed in response to regaining network connectivity.
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();
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
133 void refresh_access_token(std::string access_token, util::Optional<std::string> server_url);
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.
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.
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);
147 // Inform the sync session that it should close.
150 // Inform the sync session that it should log out.
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.
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);
161 // An object representing the user who owns the Realm this `SyncSession` represents.
162 std::shared_ptr<SyncUser> user() const
164 return m_config.user;
167 // A copy of the configuration object describing the Realm this `SyncSession` represents.
168 const SyncConfig& config() const
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
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();
184 // Return an existing external reference to this session, if one exists. Otherwise, returns `nullptr`.
185 std::shared_ptr<SyncSession> existing_external_reference();
187 // Expose some internal functionality to other parts of the ObjectStore
188 // without making it public to everyone
190 friend class _impl::RealmCoordinator;
192 static void set_sync_transact_callback(SyncSession& session,
193 std::function<SyncSessionTransactCallback> callback)
195 session.set_sync_transact_callback(std::move(callback));
198 static void nonsync_transact_notify(SyncSession& session, VersionID::version_type version)
200 session.nonsync_transact_notify(version);
204 // Expose some internal functionality to testing code.
205 struct OnlyForTesting {
206 static void handle_error(SyncSession& session, SyncError error)
208 session.handle_error(std::move(error));
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);
216 static bool has_stale_progress(SyncSession& session)
218 return session.m_current_progress != none && !session.m_latest_progress_data_is_fresh;
221 static bool has_fresh_progress(SyncSession& session)
223 return session.m_latest_progress_data_is_fresh;
228 using std::enable_shared_from_this<SyncSession>::shared_from_this;
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;
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)
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))
245 return std::make_shared<MakeSharedEnabler>(client, std::move(realm_path), std::move(config));
249 SyncSession(_impl::SyncClient&, std::string realm_path, SyncConfig);
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);
258 void set_sync_transact_callback(std::function<SyncSessionTransactCallback>);
259 void nonsync_transact_notify(VersionID::version_type);
261 void advance_state(std::unique_lock<std::mutex>& lock, const State&);
263 void create_sync_session();
264 void unregister(std::unique_lock<std::mutex>& lock);
265 void did_drop_external_reference();
267 std::function<SyncSessionTransactCallback> m_sync_transact_callback;
269 // How many bytes are uploadable or downloadable.
272 uint64_t downloadable;
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;
282 NotifierType direction;
283 util::Optional<uint64_t> captured_transferrable;
285 void update(const Progress&, bool);
286 std::function<void()> create_invocation(const Progress&, bool&) const;
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;
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
299 util::Optional<Progress> m_current_progress;
301 std::unordered_map<uint64_t, NotifierPackage> m_notifiers;
303 mutable std::mutex m_state_mutex;
304 mutable std::mutex m_progress_notifier_mutex;
306 const State* m_state = nullptr;
307 size_t m_death_count = 0;
311 std::string m_realm_path;
312 _impl::SyncClient& m_client;
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;
319 std::vector<CompletionWaitPackage> m_completion_wait_packages;
321 struct ServerOverride {
325 util::Optional<ServerOverride> m_server_override;
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;
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;
338 util::Optional<int_fast64_t> m_deferred_commit_notification;
339 bool m_deferred_close = false;
341 // The fully-resolved URL of this Realm, including the server and the path.
342 util::Optional<std::string> m_server_url;
344 std::string m_multiplex_identity;
346 class ExternalReference;
347 std::weak_ptr<ExternalReference> m_external_reference;
352 #endif // REALM_OS_SYNC_SESSION_HPP