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_UTIL_INTERPROCESS_CONDVAR
20 #define REALM_UTIL_INTERPROCESS_CONDVAR
23 #include <realm/util/features.h>
24 #include <realm/util/thread.hpp>
25 #include <realm/util/interprocess_mutex.hpp>
31 // Condvar Emulation is required if RobustMutex emulation is enabled
32 #if defined(REALM_ROBUST_MUTEX_EMULATION) || defined(_WIN32)
33 #define REALM_CONDVAR_EMULATION
40 /// Condition variable for use in synchronization monitors.
41 /// This condition variable uses emulation based on named pipes
42 /// for the inter-process case, if enabled by REALM_CONDVAR_EMULATION.
44 /// FIXME: This implementation will never release/delete pipes. This is unlikely
45 /// to be a problem as long as only a modest number of different database names
48 /// A InterprocessCondVar is always process shared.
49 class InterprocessCondVar {
51 InterprocessCondVar();
52 ~InterprocessCondVar() noexcept;
54 // Disable copying. Copying an open file will create a scenario
55 // where the same file descriptor will be opened once but closed twice.
56 InterprocessCondVar(const InterprocessCondVar&) = delete;
57 InterprocessCondVar& operator=(const InterprocessCondVar&) = delete;
59 /// To use the InterprocessCondVar, you also must place a structure of type
60 /// InterprocessCondVar::SharedPart in memory shared by multiple processes
61 /// or in a memory mapped file, and use set_shared_part() to associate
62 /// the condition variable with it's shared part. You must initialize
63 /// the shared part using InterprocessCondVar::init_shared_part(), but only before
64 /// first use and only when you have exclusive access to the shared part.
66 #ifdef REALM_CONDVAR_EMULATION
69 // Number of waiting threads.
70 int32_t m_waiters_count;
71 size_t m_was_broadcast;
73 uint64_t signal_counter;
74 uint64_t wait_counter;
78 typedef CondVar SharedPart;
81 /// You need to bind the emulation to a SharedPart in shared/mmapped memory.
82 /// The SharedPart is assumed to have been initialized (possibly by another process)
83 /// earlier through a call to init_shared_part.
84 void set_shared_part(SharedPart& shared_part, std::string path, std::string condvar_name, std::string tmp_path);
86 /// Initialize the shared part of a process shared condition variable.
87 /// A process shared condition variables may be represented by any number of
88 /// InterprocessCondVar instances in any number of different processes,
89 /// all sharing a common SharedPart instance, which must be in shared memory.
90 static void init_shared_part(SharedPart& shared_part);
92 /// Release any system resources allocated for the shared part. This should
93 /// be used *only* when you are certain, that nobody is using it.
94 void release_shared_part();
96 /// Wait for someone to call notify() or notify_all() on this condition
97 /// variable. The call to wait() may return spuriously, so the caller should
98 /// always re-evaluate the condition on which to wait and loop on wait()
100 void wait(InterprocessMutex& m, const struct timespec* tp);
102 /// If any threads are waiting for this condition, wake up at least one.
103 /// (Current implementation may actually wake all :-O ). The caller must
104 /// hold the lock associated with the condvar at the time of calling notify()
105 void notify() noexcept;
107 /// Wake up every thread that is currently waiting on this condition.
108 /// The caller must hold the lock associated with the condvar at the time
109 /// of calling notify_all().
110 void notify_all() noexcept;
112 /// Cleanup and release system resources if possible.
113 void close() noexcept;
116 // non-zero if a shared part has been registered (always 0 on process local instances)
117 SharedPart* m_shared_part = nullptr;
118 #ifdef REALM_CONDVAR_EMULATION
119 // keep the path to allocated system resource so we can remove them again
120 std::string m_resource_path;
121 // pipe used for emulation. When using a named pipe, m_fd_read is read-write and m_fd_write is unused.
122 // When using an anonymous pipe (currently only for tvOS) m_fd_read is read-only and m_fd_write is write-only.
127 // Semaphore used to queue up threads waiting for the condition to
130 // An auto-reset event used by the broadcast/signal thread to wait
131 // for all the waiting thread(s) to wake up and be released from the
133 HANDLE m_waiters_done = 0;
136 // Serialize access to m_waiters_count
137 InterprocessMutex m_waiters_lockcount;