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