1 /*
2 +----------------------------------------------------------------------+
3 | Swoole |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 2.0 of the Apache license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | http://www.apache.org/licenses/LICENSE-2.0.html |
9 | If you did not receive a copy of the Apache2.0 license and are unable|
10 | to obtain it through the world-wide-web, please send a note to |
11 | license@swoole.com so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Tianfeng Han <mikan.tenny@gmail.com> |
14 +----------------------------------------------------------------------+
15 */
16
17 #include <sys/file.h>
18
19 #include <queue>
20
21 #include "swoole_coroutine.h"
22 #include "swoole_coroutine_c_api.h"
23
24 using swoole::Coroutine;
25
26 class LockManager {
27 public:
28 bool lock_ex = false;
29 bool lock_sh = false;
30 std::queue<Coroutine *> queue_;
31 };
32
33 static std::unordered_map<std::string, LockManager *> lock_pool;
34
get_manager(const char * filename)35 static inline LockManager *get_manager(const char *filename) {
36 std::string key(filename);
37 auto i = lock_pool.find(key);
38 LockManager *lm;
39 if (i == lock_pool.end()) {
40 lm = new LockManager;
41 lock_pool[key] = lm;
42 } else {
43 lm = i->second;
44 }
45 return lm;
46 }
47
lock_ex(const char * filename,int fd)48 static inline int lock_ex(const char *filename, int fd) {
49 LockManager *lm = get_manager(filename);
50 if (lm->lock_ex || lm->lock_sh) {
51 Coroutine *co = Coroutine::get_current();
52 lm->queue_.push(co);
53 co->yield();
54 }
55 lm->lock_ex = true;
56 if (swoole_coroutine_flock(fd, LOCK_EX) < 0) {
57 lm->lock_ex = false;
58 return -1;
59 } else {
60 return 0;
61 }
62 }
63
lock_sh(const char * filename,int fd)64 static inline int lock_sh(const char *filename, int fd) {
65 LockManager *lm = get_manager(filename);
66 if (lm->lock_ex) {
67 Coroutine *co = Coroutine::get_current();
68 lm->queue_.push(co);
69 co->yield();
70 }
71 lm->lock_sh = true;
72 if (swoole_coroutine_flock(fd, LOCK_SH) < 0) {
73 lm->lock_sh = false;
74 return -1;
75 } else {
76 return 0;
77 }
78 }
79
lock_release(const char * filename,int fd)80 static inline int lock_release(const char *filename, int fd) {
81 std::string key(filename);
82 auto i = lock_pool.find(key);
83 if (i == lock_pool.end()) {
84 return swoole_coroutine_flock(fd, LOCK_UN);
85 }
86 LockManager *lm = i->second;
87 if (lm->queue_.empty()) {
88 delete lm;
89 lock_pool.erase(i);
90 return swoole_coroutine_flock(fd, LOCK_UN);
91 } else {
92 Coroutine *co = lm->queue_.front();
93 lm->queue_.pop();
94 int retval = swoole_coroutine_flock(fd, LOCK_UN);
95 co->resume();
96 return retval;
97 }
98 }
99
100 #ifdef LOCK_NB
lock_nb(const char * filename,int fd,int operation)101 static inline int lock_nb(const char *filename, int fd, int operation) {
102 int retval = ::flock(fd, operation | LOCK_NB);
103 if (retval == 0) {
104 LockManager *lm = get_manager(filename);
105 if (operation == LOCK_EX) {
106 lm->lock_ex = true;
107 } else {
108 lm->lock_sh = true;
109 }
110 }
111 return retval;
112 }
113 #endif
114
swoole_coroutine_flock_ex(const char * filename,int fd,int operation)115 int swoole_coroutine_flock_ex(const char *filename, int fd, int operation) {
116 Coroutine *co = Coroutine::get_current();
117 if (sw_unlikely(SwooleTG.reactor == nullptr || !co)) {
118 return ::flock(fd, operation);
119 }
120
121 const char *real = realpath(filename, sw_tg_buffer()->str);
122 if (real == nullptr) {
123 errno = ENOENT;
124 swoole_set_last_error(ENOENT);
125 return -1;
126 }
127
128 switch (operation) {
129 case LOCK_EX:
130 return lock_ex(real, fd);
131 case LOCK_SH:
132 return lock_sh(real, fd);
133 case LOCK_UN:
134 return lock_release(real, fd);
135 default:
136 #ifdef LOCK_NB
137 if (operation & LOCK_NB) {
138 return lock_nb(real, fd, operation & (~LOCK_NB));
139 }
140 #endif
141 return -1;
142 }
143
144 return 0;
145 }
146