1 #include "uwsgi.h"
2
3 extern struct uwsgi_server uwsgi;
4
5 /*
6
7 This is an high-performance memory area shared by all workers/cores/threads
8
9 Contrary to the caching subsystem it is 1-copy (caching for non-c apps is 2-copy)
10
11 Languages not allowing that kind of access should emulate it calling uwsgi_malloc and then copying it back to
12 the language object.
13
14 The memory areas could be monitored for changes (read: cores can be suspended while waiting for values)
15
16 You can configure multiple areas specifying multiple --sharedarea options
17
18 This is a very low-level api, try to use it to build higher-level primitives or rely on the caching subsystem
19
20 */
21
uwsgi_sharedarea_get_by_id(int id,uint64_t pos)22 struct uwsgi_sharedarea *uwsgi_sharedarea_get_by_id(int id, uint64_t pos) {
23 if (id > uwsgi.sharedareas_cnt-1) return NULL;
24 struct uwsgi_sharedarea *sa = uwsgi.sharedareas[id];
25 if (pos > sa->max_pos) return NULL;
26 return sa;
27 }
28
uwsgi_sharedarea_update(int id)29 int uwsgi_sharedarea_update(int id) {
30 struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, 0);
31 if (!sa) return -1;
32 sa->updates++;
33 return 0;
34 }
35
uwsgi_sharedarea_rlock(int id)36 int uwsgi_sharedarea_rlock(int id) {
37 struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, 0);
38 if (!sa) return -1;
39 uwsgi_rlock(sa->lock);
40 return 0;
41 }
42
uwsgi_sharedarea_wlock(int id)43 int uwsgi_sharedarea_wlock(int id) {
44 struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, 0);
45 if (!sa) return -1;
46 uwsgi_wlock(sa->lock);
47 return 0;
48 }
49
uwsgi_sharedarea_unlock(int id)50 int uwsgi_sharedarea_unlock(int id) {
51 struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, 0);
52 if (!sa) return -1;
53 uwsgi_rwunlock(sa->lock);
54 return 0;
55 }
56
57
uwsgi_sharedarea_read(int id,uint64_t pos,char * blob,uint64_t len)58 int64_t uwsgi_sharedarea_read(int id, uint64_t pos, char *blob, uint64_t len) {
59 struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos);
60 if (!sa) return -1;
61 if (pos + len > sa->max_pos + 1) return -1;
62 if (len == 0) len = (sa->max_pos + 1) - pos;
63 if (sa->honour_used && sa->used-pos < len) len = sa->used-pos ;
64 uwsgi_rlock(sa->lock);
65 memcpy(blob, sa->area + pos, len);
66 sa->hits++;
67 uwsgi_rwunlock(sa->lock);
68 return len;
69 }
70
uwsgi_sharedarea_write(int id,uint64_t pos,char * blob,uint64_t len)71 int uwsgi_sharedarea_write(int id, uint64_t pos, char *blob, uint64_t len) {
72 struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos);
73 if (!sa) return -1;
74 if (pos + len > sa->max_pos + 1) return -1;
75 uwsgi_wlock(sa->lock);
76 memcpy(sa->area + pos, blob, len);
77 sa->updates++;
78 uwsgi_rwunlock(sa->lock);
79 return 0;
80 }
81
uwsgi_sharedarea_read64(int id,uint64_t pos,int64_t * value)82 int uwsgi_sharedarea_read64(int id, uint64_t pos, int64_t *value) {
83 int64_t rlen = uwsgi_sharedarea_read(id, pos, (char *) value, 8);
84 return rlen == 8 ? 0 : -1;
85 }
86
uwsgi_sharedarea_write64(int id,uint64_t pos,int64_t * value)87 int uwsgi_sharedarea_write64(int id, uint64_t pos, int64_t *value) {
88 return uwsgi_sharedarea_write(id, pos, (char *) value, 8);
89 }
90
uwsgi_sharedarea_read8(int id,uint64_t pos,int8_t * value)91 int uwsgi_sharedarea_read8(int id, uint64_t pos, int8_t *value) {
92 int64_t rlen = uwsgi_sharedarea_read(id, pos, (char *) value, 1);
93 return rlen == 1 ? 0 : -1;
94 }
95
uwsgi_sharedarea_write8(int id,uint64_t pos,int8_t * value)96 int uwsgi_sharedarea_write8(int id, uint64_t pos, int8_t *value) {
97 return uwsgi_sharedarea_write(id, pos, (char *) value, 1);
98 }
99
uwsgi_sharedarea_read16(int id,uint64_t pos,int16_t * value)100 int uwsgi_sharedarea_read16(int id, uint64_t pos, int16_t *value) {
101 int64_t rlen = uwsgi_sharedarea_read(id, pos, (char *) value, 2);
102 return rlen == 2 ? 0 : -1;
103 }
104
uwsgi_sharedarea_write16(int id,uint64_t pos,int16_t * value)105 int uwsgi_sharedarea_write16(int id, uint64_t pos, int16_t *value) {
106 return uwsgi_sharedarea_write(id, pos, (char *) value, 2);
107 }
108
109
uwsgi_sharedarea_read32(int id,uint64_t pos,int32_t * value)110 int uwsgi_sharedarea_read32(int id, uint64_t pos, int32_t *value) {
111 int64_t rlen = uwsgi_sharedarea_read(id, pos, (char *) value, 4);
112 return rlen == 4 ? 0 : -1;
113 }
114
uwsgi_sharedarea_write32(int id,uint64_t pos,int32_t * value)115 int uwsgi_sharedarea_write32(int id, uint64_t pos, int32_t *value) {
116 return uwsgi_sharedarea_write(id, pos, (char *) value, 4);
117 }
118
uwsgi_sharedarea_inc8(int id,uint64_t pos,int8_t amount)119 int uwsgi_sharedarea_inc8(int id, uint64_t pos, int8_t amount) {
120 struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos);
121 if (!sa) return -1;
122 if (pos + 1 > sa->max_pos + 1) return -1;
123 uwsgi_wlock(sa->lock);
124 int8_t *n_ptr = (int8_t *) (sa->area + pos);
125 *n_ptr+=amount;
126 sa->updates++;
127 uwsgi_rwunlock(sa->lock);
128 return 0;
129 }
130
uwsgi_sharedarea_inc16(int id,uint64_t pos,int16_t amount)131 int uwsgi_sharedarea_inc16(int id, uint64_t pos, int16_t amount) {
132 struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos);
133 if (!sa) return -1;
134 if (pos + 2 > sa->max_pos + 1) return -1;
135 uwsgi_wlock(sa->lock);
136 int16_t *n_ptr = (int16_t *) (sa->area + pos);
137 *n_ptr+=amount;
138 sa->updates++;
139 uwsgi_rwunlock(sa->lock);
140 return 0;
141 }
142
uwsgi_sharedarea_inc32(int id,uint64_t pos,int32_t amount)143 int uwsgi_sharedarea_inc32(int id, uint64_t pos, int32_t amount) {
144 struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos);
145 if (!sa) return -1;
146 if (pos + 4 > sa->max_pos + 1) return -1;
147 uwsgi_wlock(sa->lock);
148 int32_t *n_ptr = (int32_t *) (sa->area + pos);
149 *n_ptr+=amount;
150 sa->updates++;
151 uwsgi_rwunlock(sa->lock);
152 return 0;
153 }
154
uwsgi_sharedarea_inc64(int id,uint64_t pos,int64_t amount)155 int uwsgi_sharedarea_inc64(int id, uint64_t pos, int64_t amount) {
156 struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos);
157 if (!sa) return -1;
158 if (pos + 4 > sa->max_pos + 1) return -1;
159 uwsgi_wlock(sa->lock);
160 int64_t *n_ptr = (int64_t *) (sa->area + pos);
161 *n_ptr+=amount;
162 sa->updates++;
163 uwsgi_rwunlock(sa->lock);
164 return 0;
165 }
166
167
uwsgi_sharedarea_dec8(int id,uint64_t pos,int8_t amount)168 int uwsgi_sharedarea_dec8(int id, uint64_t pos, int8_t amount) {
169 struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos);
170 if (!sa) return -1;
171 if (pos + 1 > sa->max_pos + 1) return -1;
172 uwsgi_wlock(sa->lock);
173 int8_t *n_ptr = (int8_t *) (sa->area + pos);
174 *n_ptr-=amount;
175 sa->updates++;
176 uwsgi_rwunlock(sa->lock);
177 return 0;
178 }
179
uwsgi_sharedarea_dec16(int id,uint64_t pos,int16_t amount)180 int uwsgi_sharedarea_dec16(int id, uint64_t pos, int16_t amount) {
181 struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos);
182 if (!sa) return -1;
183 if (pos + 2 > sa->max_pos + 1) return -1;
184 uwsgi_wlock(sa->lock);
185 int16_t *n_ptr = (int16_t *) (sa->area + pos);
186 *n_ptr-=amount;
187 sa->updates++;
188 uwsgi_rwunlock(sa->lock);
189 return 0;
190 }
191
uwsgi_sharedarea_dec32(int id,uint64_t pos,int32_t amount)192 int uwsgi_sharedarea_dec32(int id, uint64_t pos, int32_t amount) {
193 struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos);
194 if (!sa) return -1;
195 if (pos + 4 > sa->max_pos + 1) return -1;
196 uwsgi_wlock(sa->lock);
197 int32_t *n_ptr = (int32_t *) (sa->area + pos);
198 *n_ptr-=amount;
199 sa->updates++;
200 uwsgi_rwunlock(sa->lock);
201 return 0;
202 }
203
uwsgi_sharedarea_dec64(int id,uint64_t pos,int64_t amount)204 int uwsgi_sharedarea_dec64(int id, uint64_t pos, int64_t amount) {
205 struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos);
206 if (!sa) return -1;
207 if (pos + 4 > sa->max_pos + 1) return -1;
208 uwsgi_wlock(sa->lock);
209 int64_t *n_ptr = (int64_t *) (sa->area + pos);
210 *n_ptr-=amount;
211 sa->updates++;
212 uwsgi_rwunlock(sa->lock);
213 return 0;
214 }
215
216
217
218 /*
219 returns:
220 0 -> on updates
221 -1 -> on error
222 -2 -> on timeout
223 */
uwsgi_sharedarea_wait(int id,int freq,int timeout)224 int uwsgi_sharedarea_wait(int id, int freq, int timeout) {
225 int waiting = 0;
226 struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, 0);
227 if (!sa) return -1;
228 if (!freq) freq = 100;
229 uwsgi_rlock(sa->lock);
230 uint64_t updates = sa->updates;
231 uwsgi_rwunlock(sa->lock);
232 while(timeout == 0 || waiting == 0 || (timeout > 0 && waiting > 0 && (waiting/1000) < timeout)) {
233 if (uwsgi.wait_milliseconds_hook(freq)) {
234 uwsgi_rwunlock(sa->lock);
235 return -1;
236 }
237 waiting += freq;
238 // lock sa
239 uwsgi_rlock(sa->lock);
240 if (sa->updates != updates) {
241 uwsgi_rwunlock(sa->lock);
242 return 0;
243 }
244 // unlock sa
245 uwsgi_rwunlock(sa->lock);
246 }
247 return -2;
248 }
249
uwsgi_sharedarea_new_id()250 int uwsgi_sharedarea_new_id() {
251 int id = uwsgi.sharedareas_cnt;
252 uwsgi.sharedareas_cnt++;
253 if (!uwsgi.sharedareas) {
254 uwsgi.sharedareas = uwsgi_malloc(sizeof(struct uwsgi_sharedarea *));
255 }
256 else {
257 struct uwsgi_sharedarea **usa = realloc(uwsgi.sharedareas, ((sizeof(struct uwsgi_sharedarea *)) * uwsgi.sharedareas_cnt));
258 if (!usa) {
259 uwsgi_error("uwsgi_sharedarea_init()/realloc()");
260 exit(1);
261 }
262 uwsgi.sharedareas = usa;
263 }
264 return id;
265 }
266
announce_sa(struct uwsgi_sharedarea * sa)267 static struct uwsgi_sharedarea *announce_sa(struct uwsgi_sharedarea *sa) {
268 uwsgi_log("sharedarea %d created at %p (%d pages, area at %p)\n", sa->id, sa, sa->pages, sa->area);
269 return sa;
270 }
271
272
uwsgi_sharedarea_init_fd(int fd,uint64_t len,off_t offset)273 struct uwsgi_sharedarea *uwsgi_sharedarea_init_fd(int fd, uint64_t len, off_t offset) {
274 int id = uwsgi_sharedarea_new_id();
275 uwsgi.sharedareas[id] = uwsgi_calloc_shared(sizeof(struct uwsgi_sharedarea));
276 uwsgi.sharedareas[id]->area = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset);
277 if (uwsgi.sharedareas[id]->area == MAP_FAILED) {
278 uwsgi_error("uwsgi_sharedarea_init_fd()/mmap()");
279 exit(1);
280 }
281 uwsgi.sharedareas[id]->id = id;
282 uwsgi.sharedareas[id]->fd = fd;
283 uwsgi.sharedareas[id]->pages = len / (size_t) uwsgi.page_size;
284 if (len % (size_t) uwsgi.page_size != 0) uwsgi.sharedareas[id]->pages++;
285 uwsgi.sharedareas[id]->max_pos = len-1;
286 char *id_str = uwsgi_num2str(id);
287 uwsgi.sharedareas[id]->lock = uwsgi_rwlock_init(uwsgi_concat2("sharedarea", id_str));
288 free(id_str);
289 return announce_sa(uwsgi.sharedareas[id]);
290 }
291
292
uwsgi_sharedarea_init(int pages)293 struct uwsgi_sharedarea *uwsgi_sharedarea_init(int pages) {
294 int id = uwsgi_sharedarea_new_id();
295 uwsgi.sharedareas[id] = uwsgi_calloc_shared((size_t)uwsgi.page_size * (size_t)(pages + 1));
296 uwsgi.sharedareas[id]->area = ((char *) uwsgi.sharedareas[id]) + (size_t) uwsgi.page_size;
297 uwsgi.sharedareas[id]->id = id;
298 uwsgi.sharedareas[id]->fd = -1;
299 uwsgi.sharedareas[id]->pages = pages;
300 uwsgi.sharedareas[id]->max_pos = ((size_t)uwsgi.page_size * (size_t)pages) -1;
301 char *id_str = uwsgi_num2str(id);
302 uwsgi.sharedareas[id]->lock = uwsgi_rwlock_init(uwsgi_concat2("sharedarea", id_str));
303 free(id_str);
304 return announce_sa(uwsgi.sharedareas[id]);
305 }
306
uwsgi_sharedarea_init_ptr(char * area,uint64_t len)307 struct uwsgi_sharedarea *uwsgi_sharedarea_init_ptr(char *area, uint64_t len) {
308 int id = uwsgi_sharedarea_new_id();
309 uwsgi.sharedareas[id] = uwsgi_calloc_shared(sizeof(struct uwsgi_sharedarea));
310 uwsgi.sharedareas[id]->area = area;
311 uwsgi.sharedareas[id]->id = id;
312 uwsgi.sharedareas[id]->fd = -1;
313 uwsgi.sharedareas[id]->pages = len / (size_t) uwsgi.page_size;
314 if (len % (size_t) uwsgi.page_size != 0) uwsgi.sharedareas[id]->pages++;
315 uwsgi.sharedareas[id]->max_pos = len-1;
316 char *id_str = uwsgi_num2str(id);
317 uwsgi.sharedareas[id]->lock = uwsgi_rwlock_init(uwsgi_concat2("sharedarea", id_str));
318 free(id_str);
319 return announce_sa(uwsgi.sharedareas[id]);
320 }
321
uwsgi_sharedarea_init_keyval(char * arg)322 struct uwsgi_sharedarea *uwsgi_sharedarea_init_keyval(char *arg) {
323 char *s_pages = NULL;
324 char *s_file = NULL;
325 char *s_fd = NULL;
326 char *s_ptr = NULL;
327 char *s_size = NULL;
328 char *s_offset = NULL;
329 if (uwsgi_kvlist_parse(arg, strlen(arg), ',', '=',
330 "pages", &s_pages,
331 "file", &s_file,
332 "fd", &s_fd,
333 "ptr", &s_ptr,
334 "size", &s_size,
335 "offset", &s_offset,
336 NULL)) {
337 uwsgi_log("invalid sharedarea keyval syntax\n");
338 exit(1);
339 }
340
341 uint64_t len = 0;
342 off_t offset = 0;
343 int pages = 0;
344 if (s_size) {
345 if (strlen(s_size) > 2 && s_size[0] == '0' && s_size[1] == 'x') {
346 len = strtoul(s_size+2, NULL, 16);
347 }
348 else {
349 len = uwsgi_n64(s_size);
350 }
351 pages = len / (size_t) uwsgi.page_size;
352 if (len % (size_t) uwsgi.page_size != 0) pages++;
353 }
354
355 if (s_offset) {
356 if (strlen(s_offset) > 2 && s_offset[0] == '0' && s_offset[1] == 'x') {
357 offset = strtoul(s_offset+2, NULL, 16);
358 }
359 else {
360 offset = uwsgi_n64(s_offset);
361 }
362 }
363
364 if (s_pages) {
365 pages = atoi(s_pages);
366 }
367
368 char *area = NULL;
369 struct uwsgi_sharedarea *sa = NULL;
370
371 int fd = -1;
372 if (s_file) {
373 fd = open(s_file, O_RDWR|O_SYNC);
374 if (fd < 0) {
375 uwsgi_error_open(s_file);
376 exit(1);
377 }
378 }
379 else if (s_fd) {
380 fd = atoi(s_fd);
381 }
382 else if (s_ptr) {
383 }
384
385 if (pages) {
386 if (fd > -1) {
387 sa = uwsgi_sharedarea_init_fd(fd, len, offset);
388 }
389 else if (area) {
390 sa = uwsgi_sharedarea_init_ptr(area, len);
391 }
392 else {
393 sa = uwsgi_sharedarea_init(pages);
394 }
395 }
396 else {
397 uwsgi_log("you need to set a size for a sharedarea !!! [%s]\n", arg);
398 exit(1);
399 }
400
401 if (s_pages) free(s_pages);
402 if (s_file) free(s_file);
403 if (s_fd) free(s_fd);
404 if (s_ptr) free(s_ptr);
405 if (s_size) free(s_size);
406 if (s_offset) free(s_offset);
407
408 return sa;
409 }
410
411
uwsgi_sharedareas_init()412 void uwsgi_sharedareas_init() {
413 struct uwsgi_string_list *usl = NULL;
414 uwsgi_foreach(usl, uwsgi.sharedareas_list) {
415 char *is_keyval = strchr(usl->value, '=');
416 if (!is_keyval) {
417 uwsgi_sharedarea_init(atoi(usl->value));
418 }
419 else {
420 uwsgi_sharedarea_init_keyval(usl->value);
421 }
422 }
423 }
424