1 
2 /**
3  *    Copyright (C) 2018-present MongoDB, Inc.
4  *
5  *    This program is free software: you can redistribute it and/or modify
6  *    it under the terms of the Server Side Public License, version 1,
7  *    as published by MongoDB, Inc.
8  *
9  *    This program is distributed in the hope that it will be useful,
10  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *    Server Side Public License for more details.
13  *
14  *    You should have received a copy of the Server Side Public License
15  *    along with this program. If not, see
16  *    <http://www.mongodb.com/licensing/server-side-public-license>.
17  *
18  *    As a special exception, the copyright holders give permission to link the
19  *    code of portions of this program with the OpenSSL library under certain
20  *    conditions as described in each individual source file and distribute
21  *    linked combinations including the program with the OpenSSL library. You
22  *    must comply with the Server Side Public License in all respects for
23  *    all of the code used other than as permitted herein. If you modify file(s)
24  *    with this exception, you may extend this exception to your version of the
25  *    file(s), but you are not obligated to do so. If you do not wish to do so,
26  *    delete this exception statement from your version. If you delete this
27  *    exception statement from all source files in the program, then also delete
28  *    it in the license file.
29  */
30 
31 #pragma once
32 
33 #include <climits>  // For UINT_MAX
34 #include <vector>
35 
36 #include "mongo/db/concurrency/lock_manager.h"
37 #include "mongo/db/concurrency/lock_stats.h"
38 #include "mongo/stdx/thread.h"
39 
40 namespace mongo {
41 
42 /**
43  * Interface for acquiring locks. One of those objects will have to be instantiated for each
44  * request (transaction).
45  *
46  * Lock/unlock methods must always be called from a single thread.
47  */
48 class Locker {
49     MONGO_DISALLOW_COPYING(Locker);
50 
51 public:
~Locker()52     virtual ~Locker() {}
53 
54     /**
55      * Returns true if this is an instance of LockerNoop. Because LockerNoop doesn't implement many
56      * methods, some users may need to check this first to find out what is safe to call. LockerNoop
57      * is only used in unittests and for a brief period at startup, so you can assume you hold the
58      * equivalent of a MODE_X lock when using it.
59      *
60      * TODO get rid of this once we kill LockerNoop.
61      */
isNoop()62     virtual bool isNoop() const {
63         return false;
64     }
65 
66     /**
67      * Require global lock attempts to obtain tickets from 'reading' (for MODE_S and MODE_IS),
68      * and from 'writing' (for MODE_IX), which must have static lifetimes. There is no throttling
69      * for MODE_X, as there can only ever be a single locker using this mode. The throttling is
70      * intended to defend against arge drops in throughput under high load due to too much
71      * concurrency.
72      */
73     static void setGlobalThrottling(class TicketHolder* reading, class TicketHolder* writing);
74 
75     /**
76      * State for reporting the number of active and queued reader and writer clients.
77      */
78     enum ClientState { kInactive, kActiveReader, kActiveWriter, kQueuedReader, kQueuedWriter };
79 
80     /**
81      * Return whether client is holding any locks (active), or is queued on any locks or waiting
82      * for a ticket (throttled).
83      */
84     virtual ClientState getClientState() const = 0;
85 
86     virtual LockerId getId() const = 0;
87 
88     /**
89      * Get a platform-specific thread identifier of the thread which owns the this locker for
90      * tracing purposes.
91      */
92     virtual stdx::thread::id getThreadId() const = 0;
93 
94     /**
95      * This should be the first method invoked for a particular Locker object. It acquires the
96      * Global lock in the specified mode and effectively indicates the mode of the operation.
97      * This is what the lock modes on the global lock mean:
98      *
99      * IX - Regular write operation
100      * IS - Regular read operation
101      * S  - Stops all *write* activity. Used for administrative operations (repl, etc).
102      * X  - Stops all activity. Used for administrative operations (repl state changes,
103      *          shutdown, etc).
104      *
105      * This method can be called recursively, but each call to lockGlobal must be accompanied
106      * by a call to unlockGlobal.
107      *
108      * @param mode Mode in which the global lock should be acquired. Also indicates the intent
109      *              of the operation.
110      *
111      * @return LOCK_OK, if the global lock (and the flush lock, for the MMAP V1 engine) were
112      *          acquired within the specified time bound. Otherwise, the respective failure
113      *          code and neither lock will be acquired.
114      */
115     virtual LockResult lockGlobal(LockMode mode) = 0;
116 
117     /**
118      * Requests the global lock to be acquired in the specified mode.
119      *
120      * See the comments for lockBegin/Complete for more information on the semantics.
121      * The timeout indicates how long to wait for the lock to be acquired. The lockGlobalBegin
122      * method has a timeout for use with the TicketHolder, if there is one.
123      */
124     virtual LockResult lockGlobalBegin(LockMode mode, Milliseconds timeout) = 0;
125     virtual LockResult lockGlobalComplete(Milliseconds timeout) = 0;
126 
127     /**
128      * This method is used only in the MMAP V1 storage engine, otherwise it is a no-op. See the
129      * comments in the implementation for more details on how MMAP V1 journaling works.
130      */
131     virtual void lockMMAPV1Flush() = 0;
132 
133     /**
134      * Decrements the reference count on the global lock.  If the reference count on the
135      * global lock hits zero, the transaction is over, and unlockGlobal unlocks all other locks
136      * except for RESOURCE_MUTEX locks.
137      *
138      * @return true if this is the last endTransaction call (i.e., the global lock was
139      *          released); false if there are still references on the global lock. This value
140      *          should not be relied on and is only used for assertion purposes.
141      *
142      * @return false if the global lock is still held.
143      */
144     virtual bool unlockGlobal() = 0;
145 
146     /**
147      * This is only necessary for the MMAP V1 engine and in particular, the fsyncLock command
148      * which needs to first acquire the global lock in X-mode for truncating the journal and
149      * then downgrade to S before it blocks.
150      *
151      * The downgrade is necessary in order to be nice and not block readers while under
152      * fsyncLock.
153      */
154     virtual void downgradeGlobalXtoSForMMAPV1() = 0;
155 
156     /**
157      * beginWriteUnitOfWork/endWriteUnitOfWork must only be called by WriteUnitOfWork. See
158      * comments there for the semantics of units of work.
159      */
160     virtual void beginWriteUnitOfWork() = 0;
161     virtual void endWriteUnitOfWork() = 0;
162 
163     virtual bool inAWriteUnitOfWork() const = 0;
164 
165     /**
166      * Acquires lock on the specified resource in the specified mode and returns the outcome
167      * of the operation. See the details for LockResult for more information on what the
168      * different results mean.
169      *
170      * Each successful acquisition of a lock on a given resource increments the reference count
171      * of the lock. Therefore, each call, which returns LOCK_OK must be matched with a
172      * corresponding call to unlock.
173      *
174      * @param resId Id of the resource to be locked.
175      * @param mode Mode in which the resource should be locked. Lock upgrades are allowed.
176      * @param timeout How long to wait for the lock to be granted, before
177      *              returning LOCK_TIMEOUT. This parameter defaults to an infinite timeout.
178      *              If Milliseconds(0) is passed, the request will return immediately, if
179      *              the request could not be granted right away.
180      * @param checkDeadlock Whether to enable deadlock detection for this acquisition. This
181      *              parameter is put in place until we can handle deadlocks at all places,
182      *              which acquire locks.
183      *
184      * @return All LockResults except for LOCK_WAITING, because it blocks.
185      */
186     virtual LockResult lock(ResourceId resId,
187                             LockMode mode,
188                             Milliseconds timeout = Milliseconds::max(),
189                             bool checkDeadlock = false) = 0;
190 
191     /**
192      * Downgrades the specified resource's lock mode without changing the reference count.
193      */
194     virtual void downgrade(ResourceId resId, LockMode newMode) = 0;
195 
196     /**
197      * Releases a lock previously acquired through a lock call. It is an error to try to
198      * release lock which has not been previously acquired (invariant violation).
199      *
200      * @return true if the lock was actually released; false if only the reference count was
201      *              decremented, but the lock is still held.
202      */
203     virtual bool unlock(ResourceId resId) = 0;
204 
205     /**
206      * Retrieves the mode in which a lock is held or checks whether the lock held for a
207      * particular resource covers the specified mode.
208      *
209      * For example isLockHeldForMode will return true for MODE_S, if MODE_X is already held,
210      * because MODE_X covers MODE_S.
211      */
212     virtual LockMode getLockMode(ResourceId resId) const = 0;
213     virtual bool isLockHeldForMode(ResourceId resId, LockMode mode) const = 0;
214 
215     // These are shortcut methods for the above calls. They however check that the entire
216     // hierarchy is properly locked and because of this they are very expensive to call.
217     // Do not use them in performance critical code paths.
218     virtual bool isDbLockedForMode(StringData dbName, LockMode mode) const = 0;
219     virtual bool isCollectionLockedForMode(StringData ns, LockMode mode) const = 0;
220 
221     /**
222      * Returns the resource that this locker is waiting/blocked on (if any). If the locker is
223      * not waiting for a resource the returned value will be invalid (isValid() == false).
224      */
225     virtual ResourceId getWaitingResource() const = 0;
226 
227     /**
228      * Describes a single lock acquisition for reporting/serialization purposes.
229      */
230     struct OneLock {
231         // What lock resource is held?
232         ResourceId resourceId;
233 
234         // In what mode is it held?
235         LockMode mode;
236 
237         // Reporting/serialization order is by resourceId, which is the canonical locking order
238         bool operator<(const OneLock& rhs) const {
239             return resourceId < rhs.resourceId;
240         }
241     };
242 
243     /**
244      * Returns information and locking statistics for this instance of the locker. Used to
245      * support the db.currentOp view. This structure is not thread-safe and ideally should
246      * be used only for obtaining the necessary information and then discarded instead of
247      * reused.
248      */
249     struct LockerInfo {
250         // List of high-level locks held by this locker, sorted by ResourceId
251         std::vector<OneLock> locks;
252 
253         // If isValid(), then what lock this particular locker is sleeping on
254         ResourceId waitingResource;
255 
256         // Lock timing statistics
257         SingleThreadedLockStats stats;
258     };
259 
260     /**
261      * lockStatsBase is the snapshot of the lock stats taken at the point when the operation starts.
262      * The precise lock stats of a sub-operation would be the stats from the locker info minus the
263      * lockStatsBase.
264      */
265     virtual void getLockerInfo(
266         LockerInfo* lockerInfo,
267         const boost::optional<SingleThreadedLockStats> lockStatsBase) const = 0;
268 
269     /**
270      * LockSnapshot captures the state of all resources that are locked, what modes they're
271      * locked in, and how many times they've been locked in that mode.
272      */
273     struct LockSnapshot {
274         // The global lock is handled differently from all other locks.
275         LockMode globalMode;
276 
277         // The non-global non-flush locks held, sorted by granularity.  That is, locks[i] is
278         // coarser or as coarse as locks[i + 1].
279         std::vector<OneLock> locks;
280     };
281 
282     /**
283      * Retrieves all locks held by this transaction, other than RESOURCE_MUTEX locks, and what mode
284      * they're held in.
285      * Stores these locks in 'stateOut', destroying any previous state.  Unlocks all locks
286      * held by this transaction.  This functionality is used for yielding, which is
287      * voluntary/cooperative lock release and reacquisition in order to allow for interleaving
288      * of otherwise conflicting long-running operations.
289      *
290      * This functionality is also used for releasing locks on databases and collections
291      * when cursors are dormant and waiting for a getMore request.
292      *
293      * Returns true if locks are released.  It is expected that restoreLockerImpl will be called
294      * in the future.
295      *
296      * Returns false if locks are not released.  restoreLockState(...) does not need to be
297      * called in this case.
298      */
299     virtual bool saveLockStateAndUnlock(LockSnapshot* stateOut) = 0;
300 
301     /**
302      * Re-locks all locks whose state was stored in 'stateToRestore'.
303      */
304     virtual void restoreLockState(const LockSnapshot& stateToRestore) = 0;
305 
306     //
307     // These methods are legacy from LockerImpl and will eventually go away or be converted to
308     // calls into the Locker methods
309     //
310 
311     virtual void dump() const = 0;
312 
313     virtual bool isW() const = 0;
314     virtual bool isR() const = 0;
315 
316     virtual bool isLocked() const = 0;
317     virtual bool isWriteLocked() const = 0;
318     virtual bool isReadLocked() const = 0;
319     virtual bool isGlobalLockedRecursively() = 0;
320 
321     /**
322      * Pending means we are currently trying to get a lock (could be the parallel batch writer
323      * lock).
324      */
325     virtual bool hasLockPending() const = 0;
326 
327     /**
328      * If set to false, this opts out of conflicting with replication's use of the
329      * ParallelBatchWriterMode lock. Code that opts-out must be ok with seeing an inconsistent view
330      * of data because within a batch, secondaries apply operations in a different order than on the
331      * primary. User operations should *never* opt out.
332      */
setShouldConflictWithSecondaryBatchApplication(bool newValue)333     void setShouldConflictWithSecondaryBatchApplication(bool newValue) {
334         _shouldConflictWithSecondaryBatchApplication = newValue;
335     }
shouldConflictWithSecondaryBatchApplication()336     bool shouldConflictWithSecondaryBatchApplication() const {
337         return _shouldConflictWithSecondaryBatchApplication;
338     }
339 
340     /**
341      * If set to false, this opts out of the ticket mechanism. This should be used sparingly
342      * for special purpose threads, such as FTDC.
343      */
setShouldAcquireTicket(bool newValue)344     void setShouldAcquireTicket(bool newValue) {
345         invariant(!isLocked());
346         _shouldAcquireTicket = newValue;
347     }
shouldAcquireTicket()348     bool shouldAcquireTicket() const {
349         return _shouldAcquireTicket;
350     }
351 
352 
353 protected:
Locker()354     Locker() {}
355 
356 private:
357     bool _shouldConflictWithSecondaryBatchApplication = true;
358     bool _shouldAcquireTicket = true;
359 };
360 
361 }  // namespace mongo
362