added iOS source code
[wl-app.git] / iOS / Pods / Realm / include / core / realm / util / interprocess_mutex.hpp
diff --git a/iOS/Pods/Realm/include/core/realm/util/interprocess_mutex.hpp b/iOS/Pods/Realm/include/core/realm/util/interprocess_mutex.hpp
new file mode 100644 (file)
index 0000000..588cf00
--- /dev/null
@@ -0,0 +1,375 @@
+/*************************************************************************
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+#ifndef REALM_UTIL_INTERPROCESS_MUTEX
+#define REALM_UTIL_INTERPROCESS_MUTEX
+
+#include <realm/util/features.h>
+#include <realm/util/thread.hpp>
+#include <realm/util/file.hpp>
+#include <realm/utilities.hpp>
+#include <mutex>
+#include <map>
+#include <iostream>
+
+// Enable this only on platforms where it might be needed
+#if REALM_PLATFORM_APPLE || REALM_ANDROID
+#define REALM_ROBUST_MUTEX_EMULATION
+#endif
+
+namespace realm {
+namespace util {
+
+// fwd decl to support friend decl below
+class InterprocessCondVar;
+
+
+/// Emulation of a Robust Mutex.
+/// A Robust Mutex is an interprocess mutex which will automatically
+/// release any locks held by a process when it crashes. Contrary to
+/// Posix robust mutexes, this robust mutex is not capable of informing
+/// participants that they have been granted a lock after a crash of
+/// the process holding it (though it could be added if needed).
+
+class InterprocessMutex {
+public:
+    InterprocessMutex();
+    ~InterprocessMutex() noexcept;
+
+    // Disable copying. Copying a locked Mutex will create a scenario
+    // where the same file descriptor will be locked once but unlocked twice.
+    InterprocessMutex(const InterprocessMutex&) = delete;
+    InterprocessMutex& operator=(const InterprocessMutex&) = delete;
+
+#if defined(REALM_ROBUST_MUTEX_EMULATION) || defined(_WIN32)
+    struct SharedPart {
+    };
+#else
+    using SharedPart = RobustMutex;
+#endif
+
+    /// You need to bind the emulation to a SharedPart in shared/mmapped memory.
+    /// The SharedPart is assumed to have been initialized (possibly by another process)
+    /// elsewhere.
+    void set_shared_part(SharedPart& shared_part, const std::string& path, const std::string& mutex_name);
+    void set_shared_part(SharedPart& shared_part, File&& lock_file);
+
+    /// Destroy shared object. Potentially release system resources. Caller must
+    /// ensure that the shared_part is not in use at the point of call.
+    void release_shared_part();
+
+    /// Lock the mutex. If the mutex is already locked, wait for it to be unlocked.
+    void lock();
+
+    /// Non-blocking attempt to lock the mutex. Returns true if the lock is obtained.
+    /// If the lock can not be obtained return false immediately.
+    bool try_lock();
+
+    /// Unlock the mutex
+    void unlock();
+
+    /// Attempt to check if the mutex is valid (only relevant if not emulating)
+    bool is_valid() noexcept;
+
+    static bool is_robust_on_this_platform()
+    {
+#ifdef REALM_ROBUST_MUTEX_EMULATION
+        return true; // we're faking it!
+#else
+        return RobustMutex::is_robust_on_this_platform();
+#endif
+    }
+
+private:
+#ifdef REALM_ROBUST_MUTEX_EMULATION
+    struct LockInfo {
+        File m_file;
+        Mutex m_local_mutex;
+        LockInfo() {}
+        ~LockInfo() noexcept;
+        // Disable copying.
+        LockInfo(const LockInfo&) = delete;
+        LockInfo& operator=(const LockInfo&) = delete;
+    };
+    /// InterprocessMutex created on the same file (same inode on POSIX) share the same LockInfo.
+    /// LockInfo will be saved in a static map as a weak ptr and use the UniqueID as the key.
+    /// Operations on the map need to be protected by s_mutex
+    static std::map<File::UniqueID, std::weak_ptr<LockInfo>>* s_info_map;
+    static Mutex* s_mutex;
+    /// We manually initialize these static variables when first needed,
+    /// creating them on the heap so that they last for the entire lifetime
+    /// of the process. The destructor of these is never called; the
+    /// process will clean up their memory when exiting. It is not enough
+    /// to count instances of InterprocessMutex and clean up these statics when
+    /// the count reaches zero because the program can create more
+    /// InterprocessMutex instances before the process ends, so we really need
+    /// these variables for the entire lifetime of the process.
+    static std::once_flag s_init_flag;
+    static void initialize_statics();
+
+    /// Only used for release_shared_part
+    std::string m_filename;
+    File::UniqueID m_fileuid;
+    std::shared_ptr<LockInfo> m_lock_info;
+
+    /// Free the lock info hold by this instance.
+    /// If it is the last reference, underly resources will be freed as well.
+    void free_lock_info();
+#else
+    SharedPart* m_shared_part = nullptr;
+
+#ifdef _WIN32
+    HANDLE m_handle = 0;
+#endif
+
+#endif
+    friend class InterprocessCondVar;
+};
+
+inline InterprocessMutex::InterprocessMutex()
+{
+#ifdef REALM_ROBUST_MUTEX_EMULATION
+    std::call_once(s_init_flag, initialize_statics);
+#endif
+}
+
+inline InterprocessMutex::~InterprocessMutex() noexcept
+{
+#ifdef _WIN32
+    if (m_handle) {
+        bool b = CloseHandle(m_handle);
+        REALM_ASSERT_RELEASE(b);
+    }
+#endif
+
+#ifdef REALM_ROBUST_MUTEX_EMULATION
+    free_lock_info();
+#endif
+}
+
+#ifdef REALM_ROBUST_MUTEX_EMULATION
+inline InterprocessMutex::LockInfo::~LockInfo() noexcept
+{
+    if (m_file.is_attached()) {
+        m_file.close();
+    }
+}
+
+inline void InterprocessMutex::free_lock_info()
+{
+    // It has not been initialized yet.
+    if (!m_lock_info)
+        return;
+
+    std::lock_guard<Mutex> guard(*s_mutex);
+
+    m_lock_info.reset();
+    if ((*s_info_map)[m_fileuid].expired()) {
+        s_info_map->erase(m_fileuid);
+    }
+    m_filename.clear();
+}
+
+inline void InterprocessMutex::initialize_statics()
+{
+    s_mutex = new Mutex();
+    s_info_map = new std::map<File::UniqueID, std::weak_ptr<LockInfo>>();
+}
+#endif
+
+inline void InterprocessMutex::set_shared_part(SharedPart& shared_part, const std::string& path,
+                                               const std::string& mutex_name)
+{
+#ifdef REALM_ROBUST_MUTEX_EMULATION
+    static_cast<void>(shared_part);
+
+    free_lock_info();
+
+    m_filename = path + "." + mutex_name + ".mx";
+
+    std::lock_guard<Mutex> guard(*s_mutex);
+
+    // Try to get the file uid if the file exists
+    if (File::get_unique_id(m_filename, m_fileuid)) {
+        auto result = s_info_map->find(m_fileuid);
+        if (result != s_info_map->end()) {
+            // File exists and the lock info has been created in the map.
+            m_lock_info = result->second.lock();
+            return;
+        }
+    }
+
+    // LockInfo has not been created yet.
+    m_lock_info = std::make_shared<LockInfo>();
+    // Always use mod_Write to open file and retreive the uid in case other process
+    // deletes the file.
+    m_lock_info->m_file.open(m_filename, File::mode_Write);
+    m_fileuid = m_lock_info->m_file.get_unique_id();
+
+    (*s_info_map)[m_fileuid] = m_lock_info;
+#elif defined(_WIN32)
+    if (m_handle) {
+        bool b = CloseHandle(m_handle);
+        REALM_ASSERT_RELEASE(b);
+    }
+    // replace backslashes because they're significant in object namespace names
+    std::string path_escaped = path;
+    std::replace(path_escaped.begin(), path_escaped.end(), '\\', '/');
+    std::string name = "Local\\realm_named_intermutex_" + path_escaped + mutex_name;
+
+    std::wstring wname(name.begin(), name.end());
+    m_handle = CreateMutexW(0, false, wname.c_str());
+    if (!m_handle) {
+        throw std::system_error(std::error_code(::GetLastError(), std::system_category()), "Error opening mutex");
+    }
+#else
+    m_shared_part = &shared_part;
+    static_cast<void>(path);
+    static_cast<void>(mutex_name);
+#endif
+}
+
+inline void InterprocessMutex::set_shared_part(SharedPart& shared_part, File&& lock_file)
+{
+#ifdef REALM_ROBUST_MUTEX_EMULATION
+    static_cast<void>(shared_part);
+
+    free_lock_info();
+
+    std::lock_guard<Mutex> guard(*s_mutex);
+
+    m_fileuid = lock_file.get_unique_id();
+    auto result = s_info_map->find(m_fileuid);
+    if (result == s_info_map->end()) {
+        m_lock_info = std::make_shared<LockInfo>();
+        m_lock_info->m_file = std::move(lock_file);
+        (*s_info_map)[m_fileuid] = m_lock_info;
+    }
+    else {
+        // File exists and the lock info has been created in the map.
+        m_lock_info = result->second.lock();
+        lock_file.close();
+    }
+#else
+    m_shared_part = &shared_part;
+    static_cast<void>(lock_file);
+#endif
+}
+
+inline void InterprocessMutex::release_shared_part()
+{
+#ifdef REALM_ROBUST_MUTEX_EMULATION
+    if (!m_filename.empty())
+        File::try_remove(m_filename);
+
+    free_lock_info();
+#else
+    m_shared_part = nullptr;
+#endif
+}
+
+inline void InterprocessMutex::lock()
+{
+#ifdef REALM_ROBUST_MUTEX_EMULATION
+    std::unique_lock<Mutex> mutex_lock(m_lock_info->m_local_mutex);
+    m_lock_info->m_file.lock_exclusive();
+    mutex_lock.release();
+#else
+
+#ifdef _WIN32
+    DWORD d = WaitForSingleObject(m_handle, INFINITE);
+    REALM_ASSERT_RELEASE(d != WAIT_FAILED);
+#else
+    REALM_ASSERT(m_shared_part);
+    m_shared_part->lock([]() {});
+#endif
+#endif
+}
+
+inline bool InterprocessMutex::try_lock()
+{
+#ifdef REALM_ROBUST_MUTEX_EMULATION
+    std::unique_lock<Mutex> mutex_lock(m_lock_info->m_local_mutex, std::try_to_lock_t());
+    if (!mutex_lock.owns_lock()) {
+        return false;
+    }
+    bool success = m_lock_info->m_file.try_lock_exclusive();
+    if (success) {
+        mutex_lock.release();
+        return true;
+    }
+    else {
+        return false;
+    }
+#else
+
+#ifdef _WIN32
+    DWORD ret = WaitForSingleObject(m_handle, 0);
+    REALM_ASSERT_RELEASE(ret != WAIT_FAILED);
+
+    if (ret == WAIT_OBJECT_0) {
+        return true;
+    }
+    else {
+        return false;
+    }
+#else
+    REALM_ASSERT(m_shared_part);
+    return m_shared_part->try_lock([]() {});
+#endif
+#endif
+}
+
+
+inline void InterprocessMutex::unlock()
+{
+#ifdef REALM_ROBUST_MUTEX_EMULATION
+    m_lock_info->m_file.unlock();
+    m_lock_info->m_local_mutex.unlock();
+#else
+#ifdef _WIN32
+    bool b = ReleaseMutex(m_handle);
+    REALM_ASSERT_RELEASE(b);
+#else
+    REALM_ASSERT(m_shared_part);
+    m_shared_part->unlock();
+#endif
+#endif
+}
+
+
+inline bool InterprocessMutex::is_valid() noexcept
+{
+#ifdef REALM_ROBUST_MUTEX_EMULATION
+    return true;
+#elif defined(_WIN32)
+    // There is no safe way of testing if the m_handle mutex handle is valid on Windows, without having bad side effects
+    // for the cases where it is indeed invalid. If m_handle contains an arbitrary value, it might by coincidence be equal
+    // to a real live handle of another kind. This excludes a try_lock implementation and many other ideas.
+    return true;
+#else
+    REALM_ASSERT(m_shared_part);
+    return m_shared_part->is_valid();
+#endif
+}
+
+
+} // namespace util
+} // namespace realm
+
+#endif // #ifndef REALM_UTIL_INTERPROCESS_MUTEX