<commit_before>////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

#include "sync/sync_session.hpp"

#include "sync/impl/sync_client.hpp"
#include "sync/impl/sync_file.hpp"
#include "sync/impl/sync_metadata.hpp"
#include "sync/sync_manager.hpp"
#include "sync/sync_user.hpp"

#include <realm/sync/client.hpp>
#include <realm/sync/protocol.hpp>


using namespace realm;
using namespace realm::_impl;
using namespace realm::_impl::sync_session_states;

using SessionWaiterPointer = void(sync::Session::*)(std::function<void(std::error_code)>);

constexpr const char SyncError::c_original_file_path_key[];
constexpr const char SyncError::c_recovery_file_path_key[];

/// A state which a `SyncSession` can currently be within. State classes handle various actions
/// and state transitions.
///
/// STATES:
///
/// WAITING_FOR_ACCESS_TOKEN: upon entering this state, the binding is informed
/// that the session wants an access token. The session is now waiting for the
/// binding to provide the token.
/// From: INACTIVE
/// To:
///    * ACTIVE: when the binding successfully refreshes the token
///    * INACTIVE: if asked to log out, or if asked to close and the stop policy
///                is Immediate.
///    * ERROR: if a fatal error occurs
///
/// ACTIVE: the session is connected to the Realm Object Server and is actively
/// transferring data.
/// From: WAITING_FOR_ACCESS_TOKEN, DYING
/// To:
///    * WAITING_FOR_ACCESS_TOKEN: if the session is informed (through the error
///                                handler) that the token expired
///    * INACTIVE: if asked to log out, or if asked to close and the stop policy
///                is Immediate.
///    * DYING: if asked to close and the stop policy is AfterChangesUploaded
///    * ERROR: if a fatal error occurs
///
/// DYING: the session is performing clean-up work in preparation to be destroyed.
/// From: ACTIVE
/// To:
///    * INACTIVE: when the clean-up work completes, if the session wasn't
///                revived, or if explicitly asked to log out before the
///                clean-up work begins
///    * ACTIVE: if the session is revived
///    * ERROR: if a fatal error occurs
///
/// INACTIVE: the user owning this session has logged out, the `sync::Session`
/// owned by this session is destroyed, and the session is quiescent.
/// Note that a session briefly enters this state before being destroyed, but
/// it can also enter this state and stay there if the user has been logged out.
/// From: initial, WAITING_FOR_ACCESS_TOKEN, ACTIVE, DYING
/// To:
///    * WAITING_FOR_ACCESS_TOKEN: if the session is revived
///    * ERROR: if a fatal error occurs
///
/// ERROR: a non-recoverable error has occurred, and this session is semantically
/// invalid. The binding must create a new session with a different configuration.
/// From: WAITING_FOR_ACCESS_TOKEN, ACTIVE, DYING, INACTIVE
/// To:
///    * (none, this is a terminal state)
///
struct SyncSession::State {
    virtual ~State() { }

    // Move the given session into this state. All state transitions MUST be carried out through this method.
    virtual void enter_state(std::unique_lock<std::mutex>&, SyncSession&) const { }

    virtual void refresh_access_token(std::unique_lock<std::mutex>&,
                                      SyncSession&, std::string,
                                      const util::Optional<std::string>&) const { }

    virtual void bind_with_admin_token(std::unique_lock<std::mutex>&,
                                       SyncSession&, const std::string&, const std::string&) const { }

    // Returns true iff the lock is still locked when the method returns.
    virtual bool access_token_expired(std::unique_lock<std::mutex>&, SyncSession&) const { return true; }

    virtual void nonsync_transact_notify(std::unique_lock<std::mutex>&, SyncSession&, sync::Session::version_type) const { }

    // Perform any work needed to reactivate a session that is not already active.
    // Returns true iff the session should ask the binding to get a token for `bind()`.
    virtual bool revive_if_needed(std::unique_lock<std::mutex>&, SyncSession&) const { return false; }

    // Perform any work needed to respond to the application regaining network connectivity.
    virtual void handle_reconnect(std::unique_lock<std::mutex>&, SyncSession&) const { };

    // The user that owns this session has been logged out, and the session should take appropriate action.
    virtual void log_out(std::unique_lock<std::mutex>&, SyncSession&) const { }

    // The session should be closed and moved to `inactive`, in accordance with its stop policy and other state.
    virtual void close(std::unique_lock<std::mutex>&, SyncSession&) const { }

    // Returns true iff the error has been fully handled and the error handler should immediately return.
    virtual bool handle_error(std::unique_lock<std::mutex>&, SyncSession&, const SyncError&) const { return false; }

    // Register a handler to wait for sync session uploads, downloads, or synchronization.
    // PRECONDITION: the session state lock must be held at the time this method is called, until after it returns.
    // Returns true iff the handler was registered, either immediately or placed in a queue for later registration.
    virtual bool wait_for_completion(SyncSession&,
                                     std::function<void(std::error_code)>,
                                     SessionWaiterPointer) const {
        return false;
    }

    static const State& waiting_for_access_token;
    static const State& active;
    static const State& dying;
    static const State& inactive;
    static const State& error;
};

struct sync_session_states::WaitingForAccessToken : public SyncSession::State {
    void enter_state(std::unique_lock<std::mutex>&, SyncSession& session) const override
    {
        session.m_deferred_close = false;
    }

    void refresh_access_token(std::unique_lock<std::mutex>& lock, SyncSession& session,
                              std::string access_token,
                              const util::Optional<std::string>& server_url) const override
    {
        REALM_ASSERT(session.m_session);
        // Since the sync session was previously unbound, it's safe to do this from the
        // calling thread.
        if (!session.m_server_url) {
            session.m_server_url = server_url;
        }
        if (session.m_session_has_been_bound) {
            session.m_session->refresh(std::move(access_token));
        } else {
            session.m_session->bind(*session.m_server_url, std::move(access_token),
                                    session.m_config.client_validate_ssl, session.m_config.ssl_trust_certificate_path);
            session.m_session_has_been_bound = true;
        }

        // Register all the pending wait-for-completion blocks.
        for (auto& package : session.m_completion_wait_packages) {
            (*session.m_session.*package.waiter)(std::move(package.callback));
        }
        session.m_completion_wait_packages.clear();

        // Handle any deferred commit notification.
        if (session.m_deferred_commit_notification) {
            session.m_session->nonsync_transact_notify(*session.m_deferred_commit_notification);
            session.m_deferred_commit_notification = util::none;
        }

        session.advance_state(lock, active);
        if (session.m_deferred_close) {
            session.m_state->close(lock, session);
        }
    }

    void log_out(std::unique_lock<std::mutex>& lock, SyncSession& session) const override
    {
        session.advance_state(lock, inactive);
    }

    bool revive_if_needed(std::unique_lock<std::mutex>&, SyncSession& session) const override
    {
        session.m_deferred_close = false;
        return true;
    }

    void handle_reconnect(std::unique_lock<std::mutex>& lock, SyncSession& session) const override
    {
        // Ask the binding to retry getting the token for this session.
        std::shared_ptr<SyncSession> session_ptr = session.shared_from_this();
        lock.unlock();
        session.m_config.bind_session_handler(session_ptr->m_realm_path, session_ptr->m_config, session_ptr);
    }

    void nonsync_transact_notify(std::unique_lock<std::mutex>&,
                                 SyncSession& session,
                                 sync::Session::version_type version) const override
    {
        // Notify at first available opportunity.
        session.m_deferred_commit_notification = version;
    }

    void close(std::unique_lock<std::mutex>& lock, SyncSession& session) const override
    {<commit_msg>Fix oversight<commit_after>////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

#include "sync/sync_session.hpp"

#include "sync/impl/sync_client.hpp"
#include "sync/impl/sync_file.hpp"
#include "sync/impl/sync_metadata.hpp"
#include "sync/sync_manager.hpp"
#include "sync/sync_user.hpp"

#include <realm/sync/client.hpp>
#include <realm/sync/protocol.hpp>


using namespace realm;
using namespace realm::_impl;
using namespace realm::_impl::sync_session_states;

using SessionWaiterPointer = void(sync::Session::*)(std::function<void(std::error_code)>);

constexpr const char SyncError::c_original_file_path_key[];
constexpr const char SyncError::c_recovery_file_path_key[];

/// A state which a `SyncSession` can currently be within. State classes handle various actions
/// and state transitions.
///
/// STATES:
///
/// WAITING_FOR_ACCESS_TOKEN: upon entering this state, the binding is informed
/// that the session wants an access token. The session is now waiting for the
/// binding to provide the token.
/// From: INACTIVE
/// To:
///    * ACTIVE: when the binding successfully refreshes the token
///    * INACTIVE: if asked to log out, or if asked to close and the stop policy
///                is Immediate.
///    * ERROR: if a fatal error occurs
///
/// ACTIVE: the session is connected to the Realm Object Server and is actively
/// transferring data.
/// From: WAITING_FOR_ACCESS_TOKEN, DYING
/// To:
///    * WAITING_FOR_ACCESS_TOKEN: if the session is informed (through the error
///                                handler) that the token expired
///    * INACTIVE: if asked to log out, or if asked to close and the stop policy
///                is Immediate.
///    * DYING: if asked to close and the stop policy is AfterChangesUploaded
///    * ERROR: if a fatal error occurs
///
/// DYING: the session is performing clean-up work in preparation to be destroyed.
/// From: ACTIVE
/// To:
///    * INACTIVE: when the clean-up work completes, if the session wasn't
///                revived, or if explicitly asked to log out before the
///                clean-up work begins
///    * ACTIVE: if the session is revived
///    * ERROR: if a fatal error occurs
///
/// INACTIVE: the user owning this session has logged out, the `sync::Session`
/// owned by this session is destroyed, and the session is quiescent.
/// Note that a session briefly enters this state before being destroyed, but
/// it can also enter this state and stay there if the user has been logged out.
/// From: initial, WAITING_FOR_ACCESS_TOKEN, ACTIVE, DYING
/// To:
///    * WAITING_FOR_ACCESS_TOKEN: if the session is revived
///    * ERROR: if a fatal error occurs
///
/// ERROR: a non-recoverable error has occurred, and this session is semantically
/// invalid. The binding must create a new session with a different configuration.
/// From: WAITING_FOR_ACCESS_TOKEN, ACTIVE, DYING, INACTIVE
/// To:
///    * (none, this is a terminal state)
///
struct SyncSession::State {
    virtual ~State() { }

    // Move the given session into this state. All state transitions MUST be carried out through this method.
    virtual void enter_state(std::unique_lock<std::mutex>&, SyncSession&) const { }

    virtual void refresh_access_token(std::unique_lock<std::mutex>&,
                                      SyncSession&, std::string,
                                      const util::Optional<std::string>&) const { }

    virtual void bind_with_admin_token(std::unique_lock<std::mutex>&,
                                       SyncSession&, const std::string&, const std::string&) const { }

    // Returns true iff the lock is still locked when the method returns.
    virtual bool access_token_expired(std::unique_lock<std::mutex>&, SyncSession&) const { return true; }

    virtual void nonsync_transact_notify(std::unique_lock<std::mutex>&, SyncSession&, sync::Session::version_type) const { }

    // Perform any work needed to reactivate a session that is not already active.
    // Returns true iff the session should ask the binding to get a token for `bind()`.
    virtual bool revive_if_needed(std::unique_lock<std::mutex>&, SyncSession&) const { return false; }

    // Perform any work needed to respond to the application regaining network connectivity.
    virtual void handle_reconnect(std::unique_lock<std::mutex>&, SyncSession&) const { };

    // The user that owns this session has been logged out, and the session should take appropriate action.
    virtual void log_out(std::unique_lock<std::mutex>&, SyncSession&) const { }

    // The session should be closed and moved to `inactive`, in accordance with its stop policy and other state.
    virtual void close(std::unique_lock<std::mutex>&, SyncSession&) const { }

    // Returns true iff the error has been fully handled and the error handler should immediately return.
    virtual bool handle_error(std::unique_lock<std::mutex>&, SyncSession&, const SyncError&) const { return false; }

    // Register a handler to wait for sync session uploads, downloads, or synchronization.
    // PRECONDITION: the session state lock must be held at the time this method is called, until after it returns.
    // Returns true iff the handler was registered, either immediately or placed in a queue for later registration.
    virtual bool wait_for_completion(SyncSession&,
                                     std::function<void(std::error_code)>,
                                     SessionWaiterPointer) const {
        return false;
    }

    static const State& waiting_for_access_token;
    static const State& active;
    static const State& dying;
    static const State& inactive;
    static const State& error;
};

struct sync_session_states::WaitingForAccessToken : public SyncSession::State {
    void enter_state(std::unique_lock<std::mutex>&, SyncSession& session) const override
    {
        session.m_deferred_close = false;
    }

    void refresh_access_token(std::unique_lock<std::mutex>& lock, SyncSession& session,
                              std::string access_token,
                              const util::Optional<std::string>& server_url) const override
    {
        REALM_ASSERT(session.m_session);
        // Since the sync session was previously unbound, it's safe to do this from the
        // calling thread.
        if (!session.m_server_url) {
            session.m_server_url = server_url;
        }
        if (session.m_session_has_been_bound) {
            session.m_session->refresh(std::move(access_token));
        } else {
            session.m_session->bind(*session.m_server_url, std::move(access_token),
                                    session.m_config.client_validate_ssl, session.m_config.ssl_trust_certificate_path);
            session.m_session_has_been_bound = true;
        }

        // Register all the pending wait-for-completion blocks.
        for (auto& package : session.m_completion_wait_packages) {
            (*session.m_session.*package.waiter)(std::move(package.callback));
        }
        session.m_completion_wait_packages.clear();

        // Handle any deferred commit notification.
        if (session.m_deferred_commit_notification) {
            session.m_session->nonsync_transact_notify(*session.m_deferred_commit_notification);
            session.m_deferred_commit_notification = util::none;
        }

        session.advance_state(lock, active);
        if (session.m_deferred_close) {
            session.m_state->close(lock, session);
        }
    }

    void log_out(std::unique_lock<std::mutex>& lock, SyncSession& session) const override
    {
        session.advance_state(lock, inactive);
    }

    bool revive_if_needed(std::unique_lock<std::mutex>&, SyncSession& session) const override
    {
        session.m_deferred_close = false;
        return false;
    }

    void handle_reconnect(std::unique_lock<std::mutex>& lock, SyncSession& session) const override
    {
        // Ask the binding to retry getting the token for this session.
        std::shared_ptr<SyncSession> session_ptr = session.shared_from_this();
        lock.unlock();
        session.m_config.bind_session_handler(session_ptr->m_realm_path, session_ptr->m_config, session_ptr);
    }

    void nonsync_transact_notify(std::unique_lock<std::mutex>&,
                                 SyncSession& session,
                                 sync::Session::version_type version) const override
    {
        // Notify at first available opportunity.
        session.m_deferred_commit_notification = version;
    }

    void close(std::unique_lock<std::mutex>& lock, SyncSession& session) const override
    {
{'fraction_non_alphanumeric': 0.11330867770809884, 'fraction_numerical': 0.0008575869646781369, 'mean_word_length': 2.7286171063149482, 'pattern_counts': {'":': 0, '<': 51, '<?xml version=': 0, '>': 63, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4594148', 'n_tokens_mistral': 5047, 'n_tokens_neox': 4931, 'n_words': 1669}