#include extern struct uwsgi_server uwsgi; /* This is an high-performance memory area shared by all workers/cores/threads Contrary to the caching subsystem it is 1-copy (caching for non-c apps is 2-copy) Languages not allowing that kind of access should emulate it calling uwsgi_malloc and then copying it back to the language object. The memory areas could be monitored for changes (read: cores can be suspended while waiting for values) You can configure multiple areas specifying multiple --sharedarea options This is a very low-level api, try to use it to build higher-level primitives or rely on the caching subsystem */ struct uwsgi_sharedarea *uwsgi_sharedarea_get_by_id(int id, uint64_t pos) { if (id > uwsgi.sharedareas_cnt-1) return NULL; struct uwsgi_sharedarea *sa = uwsgi.sharedareas[id]; if (pos > sa->max_pos) return NULL; return sa; } int uwsgi_sharedarea_update(int id) { struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, 0); if (!sa) return -1; sa->updates++; return 0; } int uwsgi_sharedarea_rlock(int id) { struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, 0); if (!sa) return -1; uwsgi_rlock(sa->lock); return 0; } int uwsgi_sharedarea_wlock(int id) { struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, 0); if (!sa) return -1; uwsgi_wlock(sa->lock); return 0; } int uwsgi_sharedarea_unlock(int id) { struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, 0); if (!sa) return -1; uwsgi_rwunlock(sa->lock); return 0; } int64_t uwsgi_sharedarea_read(int id, uint64_t pos, char *blob, uint64_t len) { struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos); if (!sa) return -1; if (pos + len > sa->max_pos + 1) return -1; if (len == 0) len = (sa->max_pos + 1) - pos; if (sa->honour_used && sa->used-pos < len) len = sa->used-pos ; uwsgi_rlock(sa->lock); memcpy(blob, sa->area + pos, len); sa->hits++; uwsgi_rwunlock(sa->lock); return len; } int uwsgi_sharedarea_write(int id, uint64_t pos, char *blob, uint64_t len) { struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos); if (!sa) return -1; if (pos + len > sa->max_pos + 1) return -1; uwsgi_wlock(sa->lock); memcpy(sa->area + pos, blob, len); sa->updates++; uwsgi_rwunlock(sa->lock); return 0; } int uwsgi_sharedarea_read64(int id, uint64_t pos, int64_t *value) { int64_t rlen = uwsgi_sharedarea_read(id, pos, (char *) value, 8); return rlen == 8 ? 0 : -1; } int uwsgi_sharedarea_write64(int id, uint64_t pos, int64_t *value) { return uwsgi_sharedarea_write(id, pos, (char *) value, 8); } int uwsgi_sharedarea_read8(int id, uint64_t pos, int8_t *value) { int64_t rlen = uwsgi_sharedarea_read(id, pos, (char *) value, 1); return rlen == 1 ? 0 : -1; } int uwsgi_sharedarea_write8(int id, uint64_t pos, int8_t *value) { return uwsgi_sharedarea_write(id, pos, (char *) value, 1); } int uwsgi_sharedarea_read16(int id, uint64_t pos, int16_t *value) { int64_t rlen = uwsgi_sharedarea_read(id, pos, (char *) value, 2); return rlen == 2 ? 0 : -1; } int uwsgi_sharedarea_write16(int id, uint64_t pos, int16_t *value) { return uwsgi_sharedarea_write(id, pos, (char *) value, 2); } int uwsgi_sharedarea_read32(int id, uint64_t pos, int32_t *value) { int64_t rlen = uwsgi_sharedarea_read(id, pos, (char *) value, 4); return rlen == 4 ? 0 : -1; } int uwsgi_sharedarea_write32(int id, uint64_t pos, int32_t *value) { return uwsgi_sharedarea_write(id, pos, (char *) value, 4); } int uwsgi_sharedarea_inc8(int id, uint64_t pos, int8_t amount) { struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos); if (!sa) return -1; if (pos + 1 > sa->max_pos + 1) return -1; uwsgi_wlock(sa->lock); int8_t *n_ptr = (int8_t *) (sa->area + pos); *n_ptr+=amount; sa->updates++; uwsgi_rwunlock(sa->lock); return 0; } int uwsgi_sharedarea_inc16(int id, uint64_t pos, int16_t amount) { struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos); if (!sa) return -1; if (pos + 2 > sa->max_pos + 1) return -1; uwsgi_wlock(sa->lock); int16_t *n_ptr = (int16_t *) (sa->area + pos); *n_ptr+=amount; sa->updates++; uwsgi_rwunlock(sa->lock); return 0; } int uwsgi_sharedarea_inc32(int id, uint64_t pos, int32_t amount) { struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos); if (!sa) return -1; if (pos + 4 > sa->max_pos + 1) return -1; uwsgi_wlock(sa->lock); int32_t *n_ptr = (int32_t *) (sa->area + pos); *n_ptr+=amount; sa->updates++; uwsgi_rwunlock(sa->lock); return 0; } int uwsgi_sharedarea_inc64(int id, uint64_t pos, int64_t amount) { struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos); if (!sa) return -1; if (pos + 4 > sa->max_pos + 1) return -1; uwsgi_wlock(sa->lock); int64_t *n_ptr = (int64_t *) (sa->area + pos); *n_ptr+=amount; sa->updates++; uwsgi_rwunlock(sa->lock); return 0; } int uwsgi_sharedarea_dec8(int id, uint64_t pos, int8_t amount) { struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos); if (!sa) return -1; if (pos + 1 > sa->max_pos + 1) return -1; uwsgi_wlock(sa->lock); int8_t *n_ptr = (int8_t *) (sa->area + pos); *n_ptr-=amount; sa->updates++; uwsgi_rwunlock(sa->lock); return 0; } int uwsgi_sharedarea_dec16(int id, uint64_t pos, int16_t amount) { struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos); if (!sa) return -1; if (pos + 2 > sa->max_pos + 1) return -1; uwsgi_wlock(sa->lock); int16_t *n_ptr = (int16_t *) (sa->area + pos); *n_ptr-=amount; sa->updates++; uwsgi_rwunlock(sa->lock); return 0; } int uwsgi_sharedarea_dec32(int id, uint64_t pos, int32_t amount) { struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos); if (!sa) return -1; if (pos + 4 > sa->max_pos + 1) return -1; uwsgi_wlock(sa->lock); int32_t *n_ptr = (int32_t *) (sa->area + pos); *n_ptr-=amount; sa->updates++; uwsgi_rwunlock(sa->lock); return 0; } int uwsgi_sharedarea_dec64(int id, uint64_t pos, int64_t amount) { struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos); if (!sa) return -1; if (pos + 4 > sa->max_pos + 1) return -1; uwsgi_wlock(sa->lock); int64_t *n_ptr = (int64_t *) (sa->area + pos); *n_ptr-=amount; sa->updates++; uwsgi_rwunlock(sa->lock); return 0; } /* returns: 0 -> on updates -1 -> on error -2 -> on timeout */ int uwsgi_sharedarea_wait(int id, int freq, int timeout) { int waiting = 0; struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, 0); if (!sa) return -1; if (!freq) freq = 100; uwsgi_rlock(sa->lock); uint64_t updates = sa->updates; uwsgi_rwunlock(sa->lock); while(timeout == 0 || waiting == 0 || (timeout > 0 && waiting > 0 && (waiting/1000) < timeout)) { if (uwsgi.wait_milliseconds_hook(freq)) { uwsgi_rwunlock(sa->lock); return -1; } waiting += freq; // lock sa uwsgi_rlock(sa->lock); if (sa->updates != updates) { uwsgi_rwunlock(sa->lock); return 0; } // unlock sa uwsgi_rwunlock(sa->lock); } return -2; } int uwsgi_sharedarea_new_id() { int id = uwsgi.sharedareas_cnt; uwsgi.sharedareas_cnt++; if (!uwsgi.sharedareas) { uwsgi.sharedareas = uwsgi_malloc(sizeof(struct uwsgi_sharedarea *)); } else { struct uwsgi_sharedarea **usa = realloc(uwsgi.sharedareas, ((sizeof(struct uwsgi_sharedarea *)) * uwsgi.sharedareas_cnt)); if (!usa) { uwsgi_error("uwsgi_sharedarea_init()/realloc()"); exit(1); } uwsgi.sharedareas = usa; } return id; } static struct uwsgi_sharedarea *announce_sa(struct uwsgi_sharedarea *sa) { uwsgi_log("sharedarea %d created at %p (%d pages, area at %p)\n", sa->id, sa, sa->pages, sa->area); return sa; } struct uwsgi_sharedarea *uwsgi_sharedarea_init_fd(int fd, uint64_t len, off_t offset) { int id = uwsgi_sharedarea_new_id(); uwsgi.sharedareas[id] = uwsgi_calloc_shared(sizeof(struct uwsgi_sharedarea)); uwsgi.sharedareas[id]->area = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset); if (uwsgi.sharedareas[id]->area == MAP_FAILED) { uwsgi_error("uwsgi_sharedarea_init_fd()/mmap()"); exit(1); } uwsgi.sharedareas[id]->id = id; uwsgi.sharedareas[id]->fd = fd; uwsgi.sharedareas[id]->pages = len / (size_t) uwsgi.page_size; if (len % (size_t) uwsgi.page_size != 0) uwsgi.sharedareas[id]->pages++; uwsgi.sharedareas[id]->max_pos = len-1; char *id_str = uwsgi_num2str(id); uwsgi.sharedareas[id]->lock = uwsgi_rwlock_init(uwsgi_concat2("sharedarea", id_str)); free(id_str); return announce_sa(uwsgi.sharedareas[id]); } struct uwsgi_sharedarea *uwsgi_sharedarea_init(int pages) { int id = uwsgi_sharedarea_new_id(); uwsgi.sharedareas[id] = uwsgi_calloc_shared((size_t)uwsgi.page_size * (size_t)(pages + 1)); uwsgi.sharedareas[id]->area = ((char *) uwsgi.sharedareas[id]) + (size_t) uwsgi.page_size; uwsgi.sharedareas[id]->id = id; uwsgi.sharedareas[id]->fd = -1; uwsgi.sharedareas[id]->pages = pages; uwsgi.sharedareas[id]->max_pos = ((size_t)uwsgi.page_size * (size_t)pages) -1; char *id_str = uwsgi_num2str(id); uwsgi.sharedareas[id]->lock = uwsgi_rwlock_init(uwsgi_concat2("sharedarea", id_str)); free(id_str); return announce_sa(uwsgi.sharedareas[id]); } struct uwsgi_sharedarea *uwsgi_sharedarea_init_ptr(char *area, uint64_t len) { int id = uwsgi_sharedarea_new_id(); uwsgi.sharedareas[id] = uwsgi_calloc_shared(sizeof(struct uwsgi_sharedarea)); uwsgi.sharedareas[id]->area = area; uwsgi.sharedareas[id]->id = id; uwsgi.sharedareas[id]->fd = -1; uwsgi.sharedareas[id]->pages = len / (size_t) uwsgi.page_size; if (len % (size_t) uwsgi.page_size != 0) uwsgi.sharedareas[id]->pages++; uwsgi.sharedareas[id]->max_pos = len-1; char *id_str = uwsgi_num2str(id); uwsgi.sharedareas[id]->lock = uwsgi_rwlock_init(uwsgi_concat2("sharedarea", id_str)); free(id_str); return announce_sa(uwsgi.sharedareas[id]); } struct uwsgi_sharedarea *uwsgi_sharedarea_init_keyval(char *arg) { char *s_pages = NULL; char *s_file = NULL; char *s_fd = NULL; char *s_ptr = NULL; char *s_size = NULL; char *s_offset = NULL; if (uwsgi_kvlist_parse(arg, strlen(arg), ',', '=', "pages", &s_pages, "file", &s_file, "fd", &s_fd, "ptr", &s_ptr, "size", &s_size, "offset", &s_offset, NULL)) { uwsgi_log("invalid sharedarea keyval syntax\n"); exit(1); } uint64_t len = 0; off_t offset = 0; int pages = 0; if (s_size) { if (strlen(s_size) > 2 && s_size[0] == '0' && s_size[1] == 'x') { len = strtoul(s_size+2, NULL, 16); } else { len = uwsgi_n64(s_size); } pages = len / (size_t) uwsgi.page_size; if (len % (size_t) uwsgi.page_size != 0) pages++; } if (s_offset) { if (strlen(s_offset) > 2 && s_offset[0] == '0' && s_offset[1] == 'x') { offset = strtoul(s_offset+2, NULL, 16); } else { offset = uwsgi_n64(s_offset); } } if (s_pages) { pages = atoi(s_pages); } char *area = NULL; struct uwsgi_sharedarea *sa = NULL; int fd = -1; if (s_file) { fd = open(s_file, O_RDWR|O_SYNC); if (fd < 0) { uwsgi_error_open(s_file); exit(1); } } else if (s_fd) { fd = atoi(s_fd); } else if (s_ptr) { } if (pages) { if (fd > -1) { sa = uwsgi_sharedarea_init_fd(fd, len, offset); } else if (area) { sa = uwsgi_sharedarea_init_ptr(area, len); } else { sa = uwsgi_sharedarea_init(pages); } } else { uwsgi_log("you need to set a size for a sharedarea !!! [%s]\n", arg); exit(1); } if (s_pages) free(s_pages); if (s_file) free(s_file); if (s_fd) free(s_fd); if (s_ptr) free(s_ptr); if (s_size) free(s_size); if (s_offset) free(s_offset); return sa; } void uwsgi_sharedareas_init() { struct uwsgi_string_list *usl = NULL; uwsgi_foreach(usl, uwsgi.sharedareas_list) { char *is_keyval = strchr(usl->value, '='); if (!is_keyval) { uwsgi_sharedarea_init(atoi(usl->value)); } else { uwsgi_sharedarea_init_keyval(usl->value); } } }