1 | #pragma once |
2 | |
3 | #include <Core/Types.h> |
4 | |
5 | #include <list> |
6 | #include <vector> |
7 | #include <mutex> |
8 | #include <condition_variable> |
9 | #include <map> |
10 | #include <string> |
11 | #include <unordered_map> |
12 | |
13 | |
14 | namespace DB |
15 | { |
16 | |
17 | class RWLockImpl; |
18 | using RWLock = std::shared_ptr<RWLockImpl>; |
19 | |
20 | |
21 | /// Implements shared lock with FIFO service |
22 | /// Can be acquired recursively (several calls for the same query) in Read mode |
23 | /// |
24 | /// NOTE: it is important to allow acquiring the same lock in Read mode without waiting if it is already |
25 | /// acquired by another thread of the same query. Otherwise the following deadlock is possible: |
26 | /// - SELECT thread 1 locks in the Read mode |
27 | /// - ALTER tries to lock in the Write mode (waits for SELECT thread 1) |
28 | /// - SELECT thread 2 tries to lock in the Read mode (waits for ALTER) |
29 | class RWLockImpl : public std::enable_shared_from_this<RWLockImpl> |
30 | { |
31 | public: |
32 | enum Type |
33 | { |
34 | Read, |
35 | Write, |
36 | }; |
37 | |
38 | static RWLock create() { return RWLock(new RWLockImpl); } |
39 | |
40 | /// Just use LockHolder::reset() to release the lock |
41 | class LockHolderImpl; |
42 | friend class LockHolderImpl; |
43 | using LockHolder = std::shared_ptr<LockHolderImpl>; |
44 | |
45 | /// Waits in the queue and returns appropriate lock |
46 | /// Empty query_id means the lock is acquired out of the query context (e.g. in a background thread). |
47 | LockHolder getLock(Type type, const String & query_id); |
48 | |
49 | /// Use as query_id to acquire a lock outside the query context. |
50 | inline static const String NO_QUERY = String(); |
51 | |
52 | private: |
53 | RWLockImpl() = default; |
54 | |
55 | struct Group; |
56 | using GroupsContainer = std::list<Group>; |
57 | using OwnerQueryIds = std::unordered_map<String, size_t>; |
58 | |
59 | /// Group of locking requests that should be granted concurrently |
60 | /// i.e. a group can contain several readers, but only one writer |
61 | struct Group |
62 | { |
63 | const Type type; |
64 | size_t refererrs; |
65 | |
66 | std::condition_variable cv; /// all locking requests of the group wait on this condvar |
67 | |
68 | explicit Group(Type type_) : type{type_}, refererrs{0} {} |
69 | }; |
70 | |
71 | GroupsContainer queue; |
72 | OwnerQueryIds owner_queries; |
73 | |
74 | mutable std::mutex mutex; |
75 | }; |
76 | |
77 | |
78 | } |
79 | |