void uv__io_poll(uv_loop_t* loop, int timeout) { #ifdef __AVM2__ printf("not support avm2"); #else struct kevent events[1024]; struct kevent* ev; struct timespec spec; unsigned int nevents; unsigned int revents; ngx_queue_t* q; uint64_t base; uint64_t diff; uv__io_t* w; int filter; int fflags; int count; int nfds; int fd; int op; int i; if (loop->nfds == 0) { assert(ngx_queue_empty(&loop->watcher_queue)); return; } nevents = 0; while (!ngx_queue_empty(&loop->watcher_queue)) { q = ngx_queue_head(&loop->watcher_queue); ngx_queue_remove(q); ngx_queue_init(q); w = ngx_queue_data(q, uv__io_t, watcher_queue); assert(w->pevents != 0); assert(w->fd >= 0); assert(w->fd < (int) loop->nwatchers); if ((w->events & UV__POLLIN) == 0 && (w->pevents & UV__POLLIN) != 0) { filter = EVFILT_READ; fflags = 0; op = EV_ADD; if (w->cb == uv__fs_event) { filter = EVFILT_VNODE; fflags = NOTE_ATTRIB | NOTE_WRITE | NOTE_RENAME | NOTE_DELETE | NOTE_EXTEND | NOTE_REVOKE; op = EV_ADD | EV_ONESHOT; /* Stop the event from firing repeatedly. */ } EV_SET(events + nevents, w->fd, filter, op, fflags, 0, 0); if (++nevents == ARRAY_SIZE(events)) { if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL)) abort(); nevents = 0; } } if ((w->events & UV__POLLOUT) == 0 && (w->pevents & UV__POLLOUT) != 0) { EV_SET(events + nevents, w->fd, EVFILT_WRITE, EV_ADD, 0, 0, 0); if (++nevents == ARRAY_SIZE(events)) { if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL)) abort(); nevents = 0; } } w->events = w->pevents; } assert(timeout >= -1); base = loop->time; count = 48; /* Benchmarks suggest this gives the best throughput. */ for (;; nevents = 0) { if (timeout != -1) { spec.tv_sec = timeout / 1000; spec.tv_nsec = (timeout % 1000) * 1000000; } nfds = kevent(loop->backend_fd, events, nevents, events, ARRAY_SIZE(events), timeout == -1 ? NULL : &spec); /* Update loop->time unconditionally. It's tempting to skip the update when * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the * operating system didn't reschedule our process while in the syscall. */ SAVE_ERRNO(uv__update_time(loop)); if (nfds == 0) { assert(timeout != -1); return; } if (nfds == -1) { if (errno != EINTR) abort(); if (timeout == 0) return; if (timeout == -1) continue; /* Interrupted by a signal. Update timeout and poll again. */ goto update_timeout; } nevents = 0; for (i = 0; i < nfds; i++) { ev = events + i; fd = ev->ident; w = loop->watchers[fd]; if (w == NULL) { /* File descriptor that we've stopped watching, disarm it. */ /* TODO batch up */ struct kevent events[1]; EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0); if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL)) if (errno != EBADF && errno != ENOENT) abort(); continue; } if (ev->filter == EVFILT_VNODE) { assert(w->events == UV__POLLIN); assert(w->pevents == UV__POLLIN); w->cb(loop, w, ev->fflags); /* XXX always uv__fs_event() */ nevents++; continue; } revents = 0; if (ev->filter == EVFILT_READ) { if (w->events & UV__POLLIN) { revents |= UV__POLLIN; w->rcount = ev->data; } else { /* TODO batch up */ struct kevent events[1]; EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0); if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL)) if (errno != ENOENT) abort(); } } if (ev->filter == EVFILT_WRITE) { if (w->events & UV__POLLOUT) { revents |= UV__POLLOUT; w->wcount = ev->data; } else { /* TODO batch up */ struct kevent events[1]; EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0); if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL)) if (errno != ENOENT) abort(); } } if (ev->flags & EV_ERROR) revents |= UV__POLLERR; if (revents == 0) continue; w->cb(loop, w, revents); nevents++; } if (nevents != 0) { if (nfds == ARRAY_SIZE(events) && --count != 0) { /* Poll for more events but don't block this time. */ timeout = 0; continue; } return; } if (timeout == 0) return; if (timeout == -1) continue; update_timeout: assert(timeout > 0); diff = loop->time - base; if (diff >= (uint64_t) timeout) return; timeout -= diff; } #endif // __AVM2__ }
static ngx_int_t ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx) { uint32_t hash; in_addr_t addr, *addrs; ngx_int_t rc; ngx_uint_t naddrs; ngx_resolver_ctx_t *next; ngx_resolver_node_t *rn; hash = ngx_crc32_short(ctx->name.data, ctx->name.len); rn = ngx_resolver_lookup_name(r, &ctx->name, hash); if (rn) { if (rn->valid >= ngx_time()) { ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolve cached"); ngx_queue_remove(&rn->queue); rn->expire = ngx_time() + r->expire; ngx_queue_insert_head(&r->name_expire_queue, &rn->queue); naddrs = rn->naddrs; if (naddrs) { /* NGX_RESOLVE_A answer */ if (naddrs != 1) { addr = 0; addrs = ngx_resolver_dup(r, rn->u.addrs, naddrs * sizeof(in_addr_t)); if (addrs == NULL) { return NGX_ERROR; } } else { addr = rn->u.addr; addrs = NULL; } ctx->next = rn->waiting; rn->waiting = NULL; /* unlock name mutex */ do { ctx->state = NGX_OK; ctx->naddrs = naddrs; ctx->addrs = (naddrs == 1) ? &ctx->addr : addrs; ctx->addr = addr; next = ctx->next; ctx->handler(ctx); ctx = next; } while (ctx); if (addrs) { ngx_resolver_free(r, addrs); } return NGX_OK; } /* NGX_RESOLVE_CNAME */ if (ctx->recursion++ < NGX_RESOLVER_MAX_RECURSION) { ctx->name.len = rn->cnlen; ctx->name.data = rn->u.cname; return ngx_resolve_name_locked(r, ctx); } ctx->next = rn->waiting; rn->waiting = NULL; /* unlock name mutex */ do { ctx->state = NGX_RESOLVE_NXDOMAIN; next = ctx->next; ctx->handler(ctx); ctx = next; } while (ctx); return NGX_OK; } if (rn->waiting) { ctx->next = rn->waiting; rn->waiting = ctx; ctx->state = NGX_AGAIN; return NGX_AGAIN; } ngx_queue_remove(&rn->queue); /* lock alloc mutex */ ngx_resolver_free_locked(r, rn->query); rn->query = NULL; if (rn->cnlen) { ngx_resolver_free_locked(r, rn->u.cname); } if (rn->naddrs > 1) { ngx_resolver_free_locked(r, rn->u.addrs); } /* unlock alloc mutex */ } else { rn = ngx_resolver_alloc(r, sizeof(ngx_resolver_node_t)); if (rn == NULL) { return NGX_ERROR; } rn->name = ngx_resolver_dup(r, ctx->name.data, ctx->name.len); if (rn->name == NULL) { ngx_resolver_free(r, rn); return NGX_ERROR; } rn->node.key = hash; rn->nlen = (u_short) ctx->name.len; rn->query = NULL; ngx_rbtree_insert(&r->name_rbtree, &rn->node); } rc = ngx_resolver_create_name_query(rn, ctx); if (rc == NGX_ERROR) { goto failed; } if (rc == NGX_DECLINED) { ngx_rbtree_delete(&r->name_rbtree, &rn->node); ngx_resolver_free(r, rn->query); ngx_resolver_free(r, rn->name); ngx_resolver_free(r, rn); ctx->state = NGX_RESOLVE_NXDOMAIN; ctx->handler(ctx); return NGX_OK; } if (ngx_resolver_send_query(r, rn) != NGX_OK) { goto failed; } if (ctx->event == NULL) { ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t)); if (ctx->event == NULL) { goto failed; } ctx->event->handler = ngx_resolver_timeout_handler; ctx->event->data = ctx; ctx->event->log = r->log; ctx->ident = -1; ngx_add_timer(ctx->event, ctx->timeout); } if (ngx_queue_empty(&r->name_resend_queue)) { ngx_add_timer(r->event, (ngx_msec_t) (r->resend_timeout * 1000)); } rn->expire = ngx_time() + r->resend_timeout; ngx_queue_insert_head(&r->name_resend_queue, &rn->queue); rn->cnlen = 0; rn->naddrs = 0; rn->valid = 0; rn->waiting = ctx; ctx->state = NGX_AGAIN; return NGX_AGAIN; failed: ngx_rbtree_delete(&r->name_rbtree, &rn->node); if (rn->query) { ngx_resolver_free(r, rn->query); } ngx_resolver_free(r, rn->name); ngx_resolver_free(r, rn); return NGX_ERROR; }
static void ngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t *pc, void *data, ngx_uint_t state) { ngx_http_upstream_keepalive_peer_data_t *kp = data; ngx_http_upstream_keepalive_cache_t *item; ngx_queue_t *q; ngx_connection_t *c; ngx_http_upstream_t *u; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, "free keepalive peer"); /* cache valid connections */ u = kp->upstream; c = pc->connection; if (state & NGX_PEER_FAILED || c == NULL || c->read->eof || c->read->error || c->read->timedout || c->write->error || c->write->timedout) { goto invalid; } if (!u->keepalive) { goto invalid; } if (ngx_handle_read_event(c->read, 0) != NGX_OK) { goto invalid; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, "free keepalive peer: saving connection %p", c); if (ngx_queue_empty(&kp->conf->free)) { q = ngx_queue_last(&kp->conf->cache); ngx_queue_remove(q); item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue); ngx_http_upstream_keepalive_close(item->connection); } else { q = ngx_queue_head(&kp->conf->free); ngx_queue_remove(q); item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue); } item->connection = c; ngx_queue_insert_head(&kp->conf->cache, q); pc->connection = NULL; if (c->read->timer_set) { ngx_del_timer(c->read); } if (c->write->timer_set) { ngx_del_timer(c->write); } c->write->handler = ngx_http_upstream_keepalive_dummy_handler; c->read->handler = ngx_http_upstream_keepalive_close_handler; c->data = item; c->idle = 1; c->log = ngx_cycle->log; c->read->log = ngx_cycle->log; c->write->log = ngx_cycle->log; c->pool->log = ngx_cycle->log; item->socklen = pc->socklen; ngx_memcpy(&item->sockaddr, pc->sockaddr, pc->socklen); if (c->read->ready) { ngx_http_upstream_keepalive_close_handler(c->read); } invalid: kp->original_free_peer(pc, kp->data, state); }
static void _read_cb(uv_stream_t* stream, ssize_t len, uv_buf_t buf) { TRACE("got data\n"); luv_object_t* self = container_of(stream, luv_object_t, h); if (ngx_queue_empty(&self->rouse)) { TRACE("empty read queue, save buffer and stop read\n"); luvL_stream_stop(self); if (len >= 0) { self->buf = buf; self->count = len; } else { if (buf.base) { free(buf.base); buf.base = NULL; buf.len = 0; } } } else { TRACE("have states waiting...\n"); ngx_queue_t* q; luv_state_t* s; q = ngx_queue_head(&self->rouse); s = ngx_queue_data(q, luv_state_t, cond); ngx_queue_remove(q); TRACE("data - len: %i\n", (int)len); lua_settop(s->L, 0); if (len >= 0) { lua_pushinteger(s->L, len); lua_pushlstring(s->L, (char*)buf.base, len); if (len == 0) luvL_stream_stop(self); } else { uv_err_t err = uv_last_error(s->loop); if (err.code == UV_EOF) { TRACE("GOT EOF\n"); lua_settop(s->L, 0); lua_pushnil(s->L); } else { lua_settop(s->L, 0); lua_pushboolean(s->L, 0); lua_pushfstring(s->L, "read: %s", uv_strerror(err)); TRACE("READ ERROR, CLOSING STREAM\n"); luvL_stream_stop(self); luvL_object_close(self); } } if (buf.base) { free(buf.base); buf.len = 0; buf.base = NULL; } TRACE("wake up state: %p\n", s); luvL_state_ready(s); } }
void uv__io_poll(uv_loop_t* loop, int timeout) { struct uv__epoll_event events[1024]; struct uv__epoll_event* pe; struct uv__epoll_event e; ngx_queue_t* q; uv__io_t* w; uint64_t base; uint64_t diff; int nevents; int count; int nfds; int fd; int op; int i; if (loop->nfds == 0) { assert(ngx_queue_empty(&loop->watcher_queue)); return; } while (!ngx_queue_empty(&loop->watcher_queue)) { q = ngx_queue_head(&loop->watcher_queue); ngx_queue_remove(q); ngx_queue_init(q); w = ngx_queue_data(q, uv__io_t, watcher_queue); assert(w->pevents != 0); assert(w->fd >= 0); assert(w->fd < (int) loop->nwatchers); /* Filter out no-op changes. This is for compatibility with the event ports * backend, see the comment in uv__io_start(). */ if (w->events == w->pevents) continue; e.events = w->pevents; e.data = w->fd; if (w->events == 0) op = UV__EPOLL_CTL_ADD; else op = UV__EPOLL_CTL_MOD; /* XXX Future optimization: do EPOLL_CTL_MOD lazily if we stop watching * events, skip the syscall and squelch the events after epoll_wait(). */ if (uv__epoll_ctl(loop->backend_fd, op, w->fd, &e)) { if (errno != EEXIST) abort(); assert(op == UV__EPOLL_CTL_ADD); /* We've reactivated a file descriptor that's been watched before. */ if (uv__epoll_ctl(loop->backend_fd, UV__EPOLL_CTL_MOD, w->fd, &e)) abort(); } w->events = w->pevents; } assert(timeout >= -1); base = loop->time; count = 48; /* Benchmarks suggest this gives the best throughput. */ for (;;) { nfds = uv__epoll_wait(loop->backend_fd, events, ARRAY_SIZE(events), timeout); /* Update loop->time unconditionally. It's tempting to skip the update when * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the * operating system didn't reschedule our process while in the syscall. */ SAVE_ERRNO(uv__update_time(loop)); if (nfds == 0) { assert(timeout != -1); return; } if (nfds == -1) { if (errno != EINTR) abort(); if (timeout == -1) continue; if (timeout == 0) return; /* Interrupted by a signal. Update timeout and poll again. */ goto update_timeout; } nevents = 0; for (i = 0; i < nfds; i++) { pe = events + i; fd = pe->data; assert(fd >= 0); assert((unsigned) fd < loop->nwatchers); w = loop->watchers[fd]; if (w == NULL) { /* File descriptor that we've stopped watching, disarm it. */ if (uv__epoll_ctl(loop->backend_fd, UV__EPOLL_CTL_DEL, fd, pe)) if (errno != EBADF && errno != ENOENT) abort(); continue; } w->cb(loop, w, pe->events); nevents++; } if (nevents != 0) { if (nfds == ARRAY_SIZE(events) && --count != 0) { /* Poll for more events but don't block this time. */ timeout = 0; continue; } return; } if (timeout == 0) return; if (timeout == -1) continue; update_timeout: assert(timeout > 0); diff = loop->time - base; if (diff >= (uint64_t) timeout) return; timeout -= diff; } }
/* ngx_http_file_cache_expire,使用了LRU,也就是队列最尾端保存的是最长时间没有被使用的,并且这个函数返回的就是一个wait值 */ static time_t ngx_http_file_cache_expire(ngx_http_file_cache_t *cache) { u_char *name, *p; size_t len; time_t now, wait; ngx_path_t *path; ngx_queue_t *q; ngx_http_file_cache_node_t *fcn; u_char key[2 * NGX_HTTP_CACHE_KEY_LEN]; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "http file cache expire"); path = cache->path; len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN; // name = ngx_alloc(len + 1, ngx_cycle->log); if (name == NULL) { return 10; } // 拷贝路径名 -- cache存放的目录,不包括子目录 ngx_memcpy(name, path->name.data, path->name.len); now = ngx_time(); ngx_shmtx_lock(&cache->shpool->mutex); // 共享内存区上锁 for ( ;; ) { // 队列空,直接返回 if (ngx_queue_empty(&cache->sh->queue)) { wait = 10; break; } // 在队列的尾部取出一个节点,检查是否已经过期 q = ngx_queue_last(&cache->sh->queue); fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue); wait = fcn->expire - now; if (wait > 0) { // wait大于0说明未过期 wait = wait > 10 ? 10 : wait; break; } // 此节点已经过期 ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "http file cache expire: #%d %d %02xd%02xd%02xd%02xd", fcn->count, fcn->exists, fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]); if (fcn->count == 0) { ngx_http_file_cache_delete(cache, q, name); continue; } if (fcn->deleting) { wait = 1; break; } // 转换文件名到acsii格式 p = ngx_hex_dump(key, (u_char *) &fcn->node.key, sizeof(ngx_rbtree_key_t)); len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t); (void) ngx_hex_dump(p, fcn->key, len); /* * abnormally exited workers may leave locked cache entries, * and although it may be safe to remove them completely, * we prefer to just move them to the top of the inactive queue */ // 将节点放入队列的头部 ngx_queue_remove(q); // 重新计算过期时间 fcn->expire = ngx_time() + cache->inactive; ngx_queue_insert_head(&cache->sh->queue, &fcn->queue); ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "ignore long locked inactive cache entry %*s, count:%d", 2 * NGX_HTTP_CACHE_KEY_LEN, key, fcn->count); } ngx_shmtx_unlock(&cache->shpool->mutex); ngx_free(name); return wait; }
void ngx_http_connection_pool_free(ngx_peer_connection_t *pc, void *data, ngx_uint_t state) { ngx_http_connection_pool_t *p = data; ngx_http_connection_pool_elt_t *item; ngx_uint_t hash, bucket_id; ngx_queue_t *q, *cache, *free; ngx_connection_t *c; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, "free keepalive peer"); /* remember failed state - peer.free() may be called more than once */ if (state & NGX_PEER_FAILED) { p->failed = 1; } /* cache valid connections */ c = pc->connection; if (p->failed || c == NULL || c->read->eof || c->read->error || c->read->timedout || c->write->error || c->write->timedout) { return; } if (ngx_handle_read_event(c->read, 0) != NGX_OK) { return; } #if (NGX_DEBUG) p->count++; #endif ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, "free keepalive peer: saving connection %p", c); hash = ngx_murmur_hash2((u_char *) pc->sockaddr, pc->socklen); bucket_id = hash % p->bucket_count; cache = &p->cache[bucket_id]; free = &p->free[bucket_id]; if (ngx_queue_empty(free)) { q = ngx_queue_last(cache); ngx_queue_remove(q); item = ngx_queue_data(q, ngx_http_connection_pool_elt_t, queue); ngx_http_connection_pool_close(item->connection); } else { q = ngx_queue_head(free); ngx_queue_remove(q); item = ngx_queue_data(q, ngx_http_connection_pool_elt_t, queue); } item->connection = c; item->free = free; ngx_queue_insert_head(cache, q); pc->connection = NULL; if (c->read->timer_set) { ngx_del_timer(c->read); } if (c->write->timer_set) { ngx_del_timer(c->write); } c->write->handler = ngx_http_connection_pool_dummy_handler; c->read->handler = ngx_http_connection_pool_close_handler; c->data = item; c->idle = 1; c->log = ngx_cycle->log; c->read->log = ngx_cycle->log; c->write->log = ngx_cycle->log; c->pool->log = ngx_cycle->log; item->socklen = pc->socklen; ngx_memcpy(&item->sockaddr, pc->sockaddr, pc->socklen); if (c->read->ready) { ngx_http_connection_pool_close_handler(c->read); } }
ngx_http_reqstat_rbnode_t * ngx_http_reqstat_rbtree_lookup(ngx_shm_zone_t *shm_zone, ngx_str_t *val) { size_t size; uint32_t hash; ngx_int_t rc, excess; ngx_time_t *tp; ngx_msec_t now; ngx_queue_t *q; ngx_msec_int_t ms; ngx_rbtree_node_t *node, *sentinel; ngx_http_reqstat_ctx_t *ctx; ngx_http_reqstat_rbnode_t *rs; ctx = shm_zone->data; hash = ngx_murmur_hash2(val->data, val->len); node = ctx->sh->rbtree.root; sentinel = ctx->sh->rbtree.sentinel; tp = ngx_timeofday(); now = (ngx_msec_t) (tp->sec * 1000 + tp->msec); ngx_shmtx_lock(&ctx->shpool->mutex); while (node != sentinel) { if (hash < node->key) { node = node->left; continue; } if (hash > node->key) { node = node->right; continue; } /* hash == node->key */ rs = (ngx_http_reqstat_rbnode_t *) &node->color; /* len < node->len */ if (val->len < (size_t) rs->len) { node = node->left; continue; } rc = ngx_strncmp(val->data, rs->data, (size_t) rs->len); if (rc == 0) { ms = (ngx_msec_int_t) (now - rs->last_visit); rs->excess = rs->excess - ngx_abs(ms) * ctx->recycle_rate / 1000 + 1000; if (rs->excess > 0) { ngx_queue_remove(&rs->visit); ngx_queue_insert_head(&ctx->sh->visit, &rs->visit); } ngx_log_debug2(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0, "reqstat lookup exist: %*s", rs->len, rs->data); ngx_shmtx_unlock(&ctx->shpool->mutex); return rs; } node = (rc < 0) ? node->left : node->right; } size = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_reqstat_rbnode_t, data) + ctx->key_len; node = ngx_slab_alloc_locked(ctx->shpool, size); if (node == NULL) { /* try to free a vacant node */ q = ngx_queue_last(&ctx->sh->visit); rs = ngx_queue_data(q, ngx_http_reqstat_rbnode_t, visit); ms = (ngx_msec_int_t) (now - rs->last_visit); excess = rs->excess - ngx_abs(ms) * ctx->recycle_rate / 1000; ngx_log_debug3(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0, "reqstat lookup try recycle: %*s, %d", rs->len, rs->data, excess); if (excess < 0) { node = (ngx_rbtree_node_t *) ((char *) rs - offsetof(ngx_rbtree_node_t, color)); ngx_rbtree_delete(&ctx->sh->rbtree, node); ngx_queue_remove(&rs->visit); ngx_queue_remove(&rs->queue); ngx_log_debug2(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0, "reqstat lookup recycle: %*s", rs->len, rs->data); ngx_memzero(node, size); } else { ngx_shmtx_unlock(&ctx->shpool->mutex); return NULL; } } node->key = hash; rs = (ngx_http_reqstat_rbnode_t *) &node->color; rs->len = ngx_min(ctx->key_len, (ssize_t) val->len); ngx_memcpy(rs->data, val->data, rs->len); ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_queue_insert_head(&ctx->sh->queue, &rs->queue); ngx_queue_insert_head(&ctx->sh->visit, &rs->visit); rs->last_visit = now; rs->excess = 1000; ngx_log_debug2(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0, "reqstat lookup build: %*s", rs->len, rs->data); ngx_shmtx_unlock(&ctx->shpool->mutex); return rs; }
ngx_flag_t ngx_buffer_cache_store_gather( ngx_buffer_cache_t* cache, u_char* key, ngx_str_t* buffers, size_t buffer_count) { ngx_buffer_cache_entry_t* entry; ngx_buffer_cache_sh_t *sh = cache->sh; ngx_str_t* cur_buffer; ngx_str_t* last_buffer; size_t buffer_size; uint32_t hash; uint32_t evictions; u_char* target_buffer; hash = ngx_crc32_short(key, BUFFER_CACHE_KEY_SIZE); ngx_shmtx_lock(&cache->shpool->mutex); if (sh->reset) { // a previous store operation was killed in progress, need to reset the cache // since the data structures may be corrupt. we can only reset the cache after // the access time expires since other processes may still be reading from / // writing to the cache if (ngx_time() < sh->access_time + CACHE_LOCK_EXPIRATION) { ngx_shmtx_unlock(&cache->shpool->mutex); return 0; } // reset the cache, leave the reset flag enabled ngx_buffer_cache_reset(sh); // update stats sh->stats.reset++; } else { // remove expired entries if (cache->expiration) { for (evictions = MAX_EVICTIONS_PER_STORE; evictions > 0; evictions--) { if (!ngx_buffer_cache_free_oldest_entry(sh, cache->expiration)) { break; } } } // make sure the entry does not already exist entry = ngx_buffer_cache_rbtree_lookup(&sh->rbtree, key, hash); if (entry != NULL) { sh->stats.store_exists++; ngx_shmtx_unlock(&cache->shpool->mutex); return 0; } // enable the reset flag before we start making any changes sh->reset = 1; } // allocate a new entry entry = ngx_buffer_cache_get_free_entry(sh); if (entry == NULL) { goto error; } // calculate the buffer size last_buffer = buffers + buffer_count; buffer_size = 0; for (cur_buffer = buffers; cur_buffer < last_buffer; cur_buffer++) { buffer_size += cur_buffer->len; } // allocate a buffer to hold the data target_buffer = ngx_buffer_cache_get_free_buffer(sh, buffer_size); if (target_buffer == NULL) { goto error; } // initialize the entry entry->state = CES_ALLOCATED; entry->node.key = hash; memcpy(entry->key, key, BUFFER_CACHE_KEY_SIZE); entry->start_offset = target_buffer; entry->buffer_size = buffer_size; // update the write position sh->buffers_write = target_buffer; // move from free_queue to used_queue ngx_queue_remove(&entry->queue_node); ngx_queue_insert_tail(&sh->used_queue, &entry->queue_node); // insert to rbtree ngx_rbtree_insert(&sh->rbtree, &entry->node); // update stats sh->stats.store_ok++; sh->stats.store_bytes += buffer_size; // Note: the memcpy is performed after releasing the lock to avoid holding the lock for a long time // setting the access time of the entry and cache prevents it from being freed sh->access_time = entry->access_time = ngx_time(); entry->write_time = ngx_time(); sh->reset = 0; ngx_shmtx_unlock(&cache->shpool->mutex); for (cur_buffer = buffers; cur_buffer < last_buffer; cur_buffer++) { target_buffer = ngx_copy(target_buffer, cur_buffer->data, cur_buffer->len); } // Note: no need to obtain the lock since state is ngx_atomic_t entry->state = CES_READY; return 1; error: sh->stats.store_err++; sh->reset = 0; ngx_shmtx_unlock(&cache->shpool->mutex); return 0; }
void uv__io_close(uv_loop_t* loop, uv__io_t* w) { uv__io_stop(loop, w, UV__POLLIN | UV__POLLOUT); ngx_queue_remove(&w->pending_queue); }
static ngx_int_t ngx_http_file_cache_exists(ngx_http_file_cache_t *cache, ngx_http_cache_t *c) { ngx_int_t rc; ngx_http_file_cache_node_t *fcn; ngx_shmtx_lock(&cache->shpool->mutex); fcn = c->node; if (fcn == NULL) { fcn = ngx_http_file_cache_lookup(cache, c->key); } if (fcn) { ngx_queue_remove(&fcn->queue); if (c->node == NULL) { fcn->uses++; fcn->count++; } if (fcn->error) { if (fcn->valid_sec < ngx_time()) { goto renew; } rc = NGX_OK; goto done; } if (fcn->exists || fcn->uses >= c->min_uses) { c->exists = fcn->exists; if (fcn->body_start) { c->body_start = fcn->body_start; } rc = NGX_OK; goto done; } rc = NGX_AGAIN; goto done; } fcn = ngx_slab_alloc_locked(cache->shpool, sizeof(ngx_http_file_cache_node_t)); if (fcn == NULL) { ngx_shmtx_unlock(&cache->shpool->mutex); (void) ngx_http_file_cache_forced_expire(cache); ngx_shmtx_lock(&cache->shpool->mutex); fcn = ngx_slab_alloc_locked(cache->shpool, sizeof(ngx_http_file_cache_node_t)); if (fcn == NULL) { rc = NGX_ERROR; goto failed; } } ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t)); ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)], NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t)); ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node); fcn->uses = 1; fcn->count = 1; fcn->updating = 0; fcn->deleting = 0; renew: rc = NGX_DECLINED; fcn->valid_msec = 0; fcn->error = 0; fcn->exists = 0; fcn->valid_sec = 0; fcn->uniq = 0; fcn->body_start = 0; fcn->fs_size = 0; done: fcn->expire = ngx_time() + cache->inactive; ngx_queue_insert_head(&cache->sh->queue, &fcn->queue); c->uniq = fcn->uniq; c->error = fcn->error; c->node = fcn; failed: ngx_shmtx_unlock(&cache->shpool->mutex); return rc; }
static ngx_int_t ngx_http_limit_req_handler(ngx_http_request_t *r) { size_t len, n; uint32_t hash; ngx_int_t rc; ngx_uint_t excess; ngx_time_t *tp; ngx_rbtree_node_t *node; ngx_http_variable_value_t *vv; ngx_http_limit_req_ctx_t *ctx; ngx_http_limit_req_node_t *lz; ngx_http_limit_req_conf_t *lzcf; if (r->main->limit_req_set) { return NGX_DECLINED; } lzcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module); if (lzcf->shm_zone == NULL) { return NGX_DECLINED; } ctx = lzcf->shm_zone->data; vv = ngx_http_get_indexed_variable(r, ctx->index); if (vv == NULL || vv->not_found) { return NGX_DECLINED; } len = vv->len; if (len == 0) { return NGX_DECLINED; } if (len > 65535) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the value of the \"%V\" variable " "is more than 65535 bytes: \"%v\"", &ctx->var, vv); return NGX_DECLINED; } r->main->limit_req_set = 1; hash = ngx_crc32_short(vv->data, len); ngx_shmtx_lock(&ctx->shpool->mutex); ngx_http_limit_req_expire(ctx, 1); rc = ngx_http_limit_req_lookup(lzcf, hash, vv->data, len, &lz); if (lz) { ngx_queue_remove(&lz->queue); ngx_queue_insert_head(ctx->queue, &lz->queue); excess = lz->excess; } else { excess = 0; } ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "limit_req: %i %ui.%03ui", rc, excess / 1000, excess % 1000); if (rc == NGX_BUSY) { ngx_shmtx_unlock(&ctx->shpool->mutex); ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "limiting requests, excess: %ui.%03ui", excess / 1000, excess % 1000); return NGX_HTTP_SERVICE_UNAVAILABLE; } if (rc == NGX_AGAIN) { ngx_shmtx_unlock(&ctx->shpool->mutex); if (lzcf->nodelay) { return NGX_DECLINED; } ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "delaying request, excess: %ui.%03ui", excess / 1000, excess % 1000); if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } r->read_event_handler = ngx_http_test_reading; r->write_event_handler = ngx_http_limit_req_delay; ngx_add_timer(r->connection->write, (ngx_msec_t) excess); return NGX_AGAIN; } if (rc == NGX_OK) { goto done; } /* rc == NGX_DECLINED */ n = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_limit_req_node_t, data) + len; node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { ngx_http_limit_req_expire(ctx, 0); node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_HTTP_SERVICE_UNAVAILABLE; } } lz = (ngx_http_limit_req_node_t *) &node->color; node->key = hash; lz->len = (u_char) len; tp = ngx_timeofday(); lz->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec); lz->excess = 0; ngx_memcpy(lz->data, vv->data, len); ngx_rbtree_insert(ctx->rbtree, node); ngx_queue_insert_head(ctx->queue, &lz->queue); done: ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_DECLINED; }
ngx_int_t ngx_http_tfs_parse_keepalive_message(ngx_http_tfs_t *t) { u_char *p, update; uint16_t type; ngx_str_t err_msg; ngx_int_t rc; ngx_queue_t *q, *queue; ngx_rbtree_node_t *node; ngx_http_tfs_header_t *header; ngx_http_tfs_rc_ctx_t *rc_ctx; ngx_http_tfs_rcs_info_t *rc_info; ngx_http_tfs_peer_connection_t *tp; header = (ngx_http_tfs_header_t *) t->header; tp = t->tfs_peer; type = header->type; switch (type) { case NGX_HTTP_TFS_STATUS_MESSAGE: ngx_str_set(&err_msg, "keepalive rc"); return ngx_http_tfs_status_message(&tp->body_buffer, &err_msg, t->log); } p = tp->body_buffer.pos; update = *p; p++; if (!update) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, t->log, 0, "rc keepalive, update flag: %d", update); } else { ngx_log_error(NGX_LOG_WARN, t->log, 0, "rc keepalive, update flag: %d", update); } rc_ctx = t->main_conf->rc_ctx; queue = &rc_ctx->sh->kp_queue; if (ngx_queue_empty(queue)) { return NGX_ERROR; } q = t->curr_ka_queue; if (q == NULL) { return NGX_ERROR; } t->curr_ka_queue = ngx_queue_next(q); if (update == NGX_HTTP_TFS_NO) { return NGX_OK; } /* FIXME: do not consider rc_info_node being expired, it hardly occurs * e.g. a single rc_info_node occupys nearly 2KB space, * 10MB for tfs_rcs_zone can hold at least 5000 rc_infos. */ rc_info = ngx_queue_data(q, ngx_http_tfs_rcs_info_t, kp_queue); /* update info node */ /* FIXME: sth terrible may happen here if someone has get the rc_info before lock */ ngx_shmtx_lock(&rc_ctx->shpool->mutex); rc = ngx_http_tfs_update_info_node(t, rc_ctx, rc_info, p); /* rc_info has been destroyed, remove from queue and rbtree */ if (rc == NGX_ERROR) { ngx_queue_remove(&rc_info->queue); ngx_queue_remove(&rc_info->kp_queue); node = (ngx_rbtree_node_t *) ((u_char *) rc_info - offsetof(ngx_rbtree_node_t, color)); ngx_rbtree_delete(&rc_ctx->sh->rbtree, node); ngx_http_tfs_rc_server_destroy_node(rc_ctx, rc_info); } ngx_shmtx_unlock(&rc_ctx->shpool->mutex); return rc; }
static int ngx_http_lua_shdict_set_helper(lua_State *L, int flags) { #if (NGX_DEBUG) ngx_http_request_t *r; #endif int i, n; ngx_str_t name; ngx_str_t key; uint32_t hash; ngx_int_t rc; ngx_http_lua_shdict_ctx_t *ctx; ngx_http_lua_shdict_node_t *sd; ngx_str_t value; int value_type; lua_Number num; u_char c; lua_Number exptime = 0; u_char *p; ngx_rbtree_node_t *node; ngx_time_t *tp; ngx_shm_zone_t *zone; int forcible = 0; /* indicates whether to foricibly override other * valid entries */ int32_t user_flags = 0; n = lua_gettop(L); if (n != 3 && n != 4 && n != 5) { return luaL_error(L, "expecting 3, 4 or 5 arguments, " "but only seen %d", n); } luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); zone = lua_touserdata(L, 1); if (zone == NULL) { return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer"); } ctx = zone->data; name = ctx->name; #if (NGX_DEBUG) lua_getglobal(L, GLOBALS_SYMBOL_REQUEST); r = lua_touserdata(L, -1); lua_pop(L, 1); #endif key.data = (u_char *) luaL_checklstring(L, 2, &key.len); if (key.len == 0) { return luaL_error(L, "attemp to use empty keys"); } if (key.len > 65535) { return luaL_error(L, "the key argument is more than 65535 bytes: %d", (int) key.len); } hash = ngx_crc32_short(key.data, key.len); value_type = lua_type(L, 3); switch (value_type) { case LUA_TSTRING: value.data = (u_char *) lua_tolstring(L, 3, &value.len); break; case LUA_TNUMBER: value.len = sizeof(lua_Number); num = lua_tonumber(L, 3); value.data = (u_char *) # break; case LUA_TBOOLEAN: value.len = sizeof(u_char); c = lua_toboolean(L, 3) ? 1 : 0; value.data = &c; break; case LUA_TNIL: if (flags & (NGX_HTTP_LUA_SHDICT_ADD|NGX_HTTP_LUA_SHDICT_REPLACE)) { return luaL_error(L, "attempt to add or replace nil values"); } value.len = 0; value.data = NULL; break; default: return luaL_error(L, "unsupported value type for key \"%s\" in " "shared_dict \"%s\": %s", key.data, name.data, lua_typename(L, value_type)); } if (n >= 4) { exptime = luaL_checknumber(L, 4); if (exptime < 0) { exptime = 0; } } if (n == 5) { user_flags = (uint32_t) luaL_checkinteger(L, 5); } dd("looking up key %s in shared dict %s", key.data, name.data); ngx_shmtx_lock(&ctx->shpool->mutex); #if 1 ngx_http_lua_shdict_expire(ctx, 1); #endif rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); dd("shdict lookup returned %d", (int) rc); if (flags & NGX_HTTP_LUA_SHDICT_REPLACE) { if (rc == NGX_DECLINED || rc == NGX_DONE) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 0); lua_pushliteral(L, "not found"); lua_pushboolean(L, forcible); return 3; } /* rc == NGX_OK */ goto replace; } if (flags & NGX_HTTP_LUA_SHDICT_ADD) { if (rc == NGX_OK) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 0); lua_pushliteral(L, "exists"); lua_pushboolean(L, forcible); return 3; } if (rc == NGX_DONE) { /* exists but expired */ dd("go to replace"); goto replace; } /* rc == NGX_DECLINED */ dd("go to insert"); goto insert; } if (rc == NGX_OK || rc == NGX_DONE) { if (value_type == LUA_TNIL) { goto remove; } replace: if (value.data && value.len == (size_t) sd->value_len) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua shared dict set: found old entry and value size matched, " "reusing it"); ngx_queue_remove(&sd->queue); ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); sd->key_len = key.len; if (exptime > 0) { tp = ngx_timeofday(); sd->expires = (ngx_msec_t) (tp->sec * 1000 + tp->msec) + (ngx_msec_t) (exptime * 1000); } else { sd->expires = 0; } sd->user_flags = user_flags; sd->value_len = (uint32_t) value.len; dd("setting value type to %d", value_type); sd->value_type = value_type; p = ngx_copy(sd->data, key.data, key.len); ngx_memcpy(p, value.data, value.len); ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 1); lua_pushnil(L); lua_pushboolean(L, forcible); return 3; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua shared dict set: found old entry bug value size NOT matched, " "removing it first"); remove: ngx_queue_remove(&sd->queue); node = (ngx_rbtree_node_t *) ((u_char *) sd - offsetof(ngx_rbtree_node_t, color)); ngx_rbtree_delete(&ctx->sh->rbtree, node); ngx_slab_free_locked(ctx->shpool, node); } insert: /* rc == NGX_DECLINED or value size unmatch */ if (value.data == NULL) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 1); lua_pushnil(L); lua_pushboolean(L, 0); return 3; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua shared dict set: creating a new entry"); n = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_lua_shdict_node_t, data) + key.len + value.len; node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua shared dict set: overriding non-expired items due to memory " "shortage for entry \"%V\"", &name); for (i = 0; i < 30; i++) { if (ngx_http_lua_shdict_expire(ctx, 0) == 0) { break; } forcible = 1; node = ngx_slab_alloc_locked(ctx->shpool, n); if (node != NULL) { goto allocated; } } ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 0); lua_pushliteral(L, "no memory"); lua_pushboolean(L, forcible); return 3; } allocated: sd = (ngx_http_lua_shdict_node_t *) &node->color; node->key = hash; sd->key_len = key.len; if (exptime > 0) { tp = ngx_timeofday(); sd->expires = (ngx_msec_t) (tp->sec * 1000 + tp->msec) + (ngx_msec_t) (exptime * 1000); } else { sd->expires = 0; } sd->user_flags = user_flags; sd->value_len = (uint32_t) value.len; dd("setting value type to %d", value_type); sd->value_type = value_type; p = ngx_copy(sd->data, key.data, key.len); ngx_memcpy(p, value.data, value.len); ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 1); lua_pushnil(L); lua_pushboolean(L, forcible); return 3; }
static int ngx_http_lua_shdict_set_helper(lua_State *L, int flags) { int i, n; ngx_str_t key; uint32_t hash; ngx_int_t rc; ngx_http_lua_shdict_ctx_t *ctx; ngx_http_lua_shdict_node_t *sd; ngx_str_t value; int value_type; double num; u_char c; lua_Number exptime = 0; u_char *p; ngx_rbtree_node_t *node; ngx_time_t *tp; ngx_shm_zone_t *zone; int forcible = 0; /* indicates whether to foricibly override other * valid entries */ int32_t user_flags = 0; n = lua_gettop(L); if (n != 3 && n != 4 && n != 5) { return luaL_error(L, "expecting 3, 4 or 5 arguments, " "but only seen %d", n); } zone = lua_touserdata(L, 1); if (zone == NULL) { return luaL_error(L, "bad \"zone\" argument"); } ctx = zone->data; if (lua_isnil(L, 2)) { lua_pushnil(L); lua_pushliteral(L, "nil key"); return 2; } key.data = (u_char *) luaL_checklstring(L, 2, &key.len); if (key.len == 0) { lua_pushnil(L); lua_pushliteral(L, "empty key"); return 2; } if (key.len > 65535) { lua_pushnil(L); lua_pushliteral(L, "key too long"); return 2; } hash = ngx_crc32_short(key.data, key.len); value_type = lua_type(L, 3); switch (value_type) { case LUA_TSTRING: value.data = (u_char *) lua_tolstring(L, 3, &value.len); break; case LUA_TNUMBER: value.len = sizeof(double); num = lua_tonumber(L, 3); value.data = (u_char *) # break; case LUA_TBOOLEAN: value.len = sizeof(u_char); c = lua_toboolean(L, 3) ? 1 : 0; value.data = &c; break; case LUA_TNIL: if (flags & (NGX_HTTP_LUA_SHDICT_ADD|NGX_HTTP_LUA_SHDICT_REPLACE)) { lua_pushnil(L); lua_pushliteral(L, "attempt to add or replace nil values"); return 2; } ngx_str_null(&value); break; default: lua_pushnil(L); lua_pushliteral(L, "bad value type"); return 2; } if (n >= 4) { exptime = luaL_checknumber(L, 4); if (exptime < 0) { exptime = 0; } } if (n == 5) { user_flags = (uint32_t) luaL_checkinteger(L, 5); } ngx_shmtx_lock(&ctx->shpool->mutex); #if 1 ngx_http_lua_shdict_expire(ctx, 1); #endif rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); dd("shdict lookup returned %d", (int) rc); if (flags & NGX_HTTP_LUA_SHDICT_REPLACE) { if (rc == NGX_DECLINED || rc == NGX_DONE) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 0); lua_pushliteral(L, "not found"); lua_pushboolean(L, forcible); return 3; } /* rc == NGX_OK */ goto replace; } if (flags & NGX_HTTP_LUA_SHDICT_ADD) { if (rc == NGX_OK) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 0); lua_pushliteral(L, "exists"); lua_pushboolean(L, forcible); return 3; } if (rc == NGX_DONE) { /* exists but expired */ dd("go to replace"); goto replace; } /* rc == NGX_DECLINED */ dd("go to insert"); goto insert; } if (rc == NGX_OK || rc == NGX_DONE) { if (value_type == LUA_TNIL) { goto remove; } replace: if (value.data && value.len == (size_t) sd->value_len) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "lua shared dict set: found old entry and value " "size matched, reusing it"); ngx_queue_remove(&sd->queue); ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); sd->key_len = (u_short) key.len; if (exptime > 0) { tp = ngx_timeofday(); sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + (uint64_t) (exptime * 1000); } else { sd->expires = 0; } sd->user_flags = user_flags; sd->value_len = (uint32_t) value.len; dd("setting value type to %d", value_type); sd->value_type = (uint8_t) value_type; p = ngx_copy(sd->data, key.data, key.len); ngx_memcpy(p, value.data, value.len); ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 1); lua_pushnil(L); lua_pushboolean(L, forcible); return 3; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "lua shared dict set: found old entry but value size " "NOT matched, removing it first"); remove: ngx_queue_remove(&sd->queue); node = (ngx_rbtree_node_t *) ((u_char *) sd - offsetof(ngx_rbtree_node_t, color)); ngx_rbtree_delete(&ctx->sh->rbtree, node); ngx_slab_free_locked(ctx->shpool, node); } insert: /* rc == NGX_DECLINED or value size unmatch */ if (value.data == NULL) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 1); lua_pushnil(L); lua_pushboolean(L, 0); return 3; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "lua shared dict set: creating a new entry"); n = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_lua_shdict_node_t, data) + key.len + value.len; node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { if (flags & NGX_HTTP_LUA_SHDICT_SAFE_STORE) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 0); lua_pushliteral(L, "no memory"); return 2; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "lua shared dict set: overriding non-expired items " "due to memory shortage for entry \"%V\"", &key); for (i = 0; i < 30; i++) { if (ngx_http_lua_shdict_expire(ctx, 0) == 0) { break; } forcible = 1; node = ngx_slab_alloc_locked(ctx->shpool, n); if (node != NULL) { goto allocated; } } ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 0); lua_pushliteral(L, "no memory"); lua_pushboolean(L, forcible); return 3; } allocated: sd = (ngx_http_lua_shdict_node_t *) &node->color; node->key = hash; sd->key_len = (u_short) key.len; if (exptime > 0) { tp = ngx_timeofday(); sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + (uint64_t) (exptime * 1000); } else { sd->expires = 0; } sd->user_flags = user_flags; sd->value_len = (uint32_t) value.len; dd("setting value type to %d", value_type); sd->value_type = (uint8_t) value_type; p = ngx_copy(sd->data, key.data, key.len); ngx_memcpy(p, value.data, value.len); ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushboolean(L, 1); lua_pushnil(L); lua_pushboolean(L, forcible); return 3; }
void uv__io_poll(uv_loop_t* loop, int timeout) { struct port_event events[1024]; struct port_event* pe; struct timespec spec; ngx_queue_t* q; uv__io_t* w; uint64_t base; uint64_t diff; unsigned int nfds; unsigned int i; int saved_errno; int nevents; int count; int fd; if (loop->nfds == 0) { assert(ngx_queue_empty(&loop->watcher_queue)); return; } while (!ngx_queue_empty(&loop->watcher_queue)) { q = ngx_queue_head(&loop->watcher_queue); ngx_queue_remove(q); ngx_queue_init(q); w = ngx_queue_data(q, uv__io_t, watcher_queue); assert(w->pevents != 0); if (port_associate(loop->backend_fd, PORT_SOURCE_FD, w->fd, w->pevents, 0)) abort(); w->events = w->pevents; } assert(timeout >= -1); base = loop->time; count = 48; /* Benchmarks suggest this gives the best throughput. */ for (;;) { if (timeout != -1) { spec.tv_sec = timeout / 1000; spec.tv_nsec = (timeout % 1000) * 1000000; } /* Work around a kernel bug where nfds is not updated. */ events[0].portev_source = 0; nfds = 1; saved_errno = 0; if (port_getn(loop->backend_fd, events, ARRAY_SIZE(events), &nfds, timeout == -1 ? NULL : &spec)) { /* Work around another kernel bug: port_getn() may return events even * on error. */ if (errno == EINTR || errno == ETIME) saved_errno = errno; else abort(); } /* Update loop->time unconditionally. It's tempting to skip the update when * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the * operating system didn't reschedule our process while in the syscall. */ SAVE_ERRNO(uv__update_time(loop)); if (events[0].portev_source == 0) { if (timeout == 0) return; if (timeout == -1) continue; goto update_timeout; } if (nfds == 0) { assert(timeout != -1); return; } nevents = 0; assert(loop->watchers != NULL); loop->watchers[loop->nwatchers] = (void*) events; loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; for (i = 0; i < nfds; i++) { pe = events + i; fd = pe->portev_object; /* Skip invalidated events, see uv__platform_invalidate_fd */ if (fd == -1) continue; assert(fd >= 0); assert((unsigned) fd < loop->nwatchers); w = loop->watchers[fd]; /* File descriptor that we've stopped watching, ignore. */ if (w == NULL) continue; w->cb(loop, w, pe->portev_events); nevents++; /* Events Ports operates in oneshot mode, rearm timer on next run. */ if (w->pevents != 0 && ngx_queue_empty(&w->watcher_queue)) ngx_queue_insert_tail(&loop->watcher_queue, &w->watcher_queue); } loop->watchers[loop->nwatchers] = NULL; loop->watchers[loop->nwatchers + 1] = NULL; if (nevents != 0) { if (nfds == ARRAY_SIZE(events) && --count != 0) { /* Poll for more events but don't block this time. */ timeout = 0; continue; } return; } if (saved_errno == ETIME) { assert(timeout != -1); return; } if (timeout == 0) return; if (timeout == -1) continue; update_timeout: assert(timeout > 0); diff = loop->time - base; if (diff >= (uint64_t) timeout) return; timeout -= diff; } }
void ngx_http_file_cache_free(ngx_http_cache_t *c, ngx_temp_file_t *tf) { ngx_http_file_cache_t *cache; ngx_http_file_cache_node_t *fcn; if (c->updated || c->node == NULL) { return; } cache = c->file_cache; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->file.log, 0, "http file cache free, fd: %d", c->file.fd); ngx_shmtx_lock(&cache->shpool->mutex); fcn = c->node; fcn->count--; if (c->updating) { fcn->updating = 0; } if (c->error) { fcn->error = c->error; if (c->valid_sec) { fcn->valid_sec = c->valid_sec; fcn->valid_msec = c->valid_msec; } } else if (!fcn->exists && fcn->count == 0 && c->min_uses == 1) { ngx_queue_remove(&fcn->queue); // 在队列中删除 ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node); // 在红黑树中删除 ngx_slab_free_locked(cache->shpool, fcn); // 释放在 ngx_http_upstream_cache() 函数中申请的 node 内存空间 c->node = NULL; } ngx_shmtx_unlock(&cache->shpool->mutex); c->updated = 1; c->updating = 0; if (c->temp_file) { if (tf && tf->file.fd != NGX_INVALID_FILE) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->file.log, 0, "http file cache incomplete: \"%s\"", tf->file.name.data); if (ngx_delete_file(tf->file.name.data) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_CRIT, c->file.log, ngx_errno, ngx_delete_file_n " \"%s\" failed", tf->file.name.data); } } } if (c->wait_event.timer_set) { ngx_del_timer(&c->wait_event); } }
ngx_int_t ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name, ngx_open_file_info_t *of, ngx_pool_t *pool) { time_t now; uint32_t hash; ngx_int_t rc; ngx_pool_cleanup_t *cln; ngx_cached_open_file_t *file; ngx_pool_cleanup_file_t *clnf; ngx_open_file_cache_cleanup_t *ofcln; of->err = 0; if (cache == NULL) { cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t)); if (cln == NULL) { return NGX_ERROR; } rc = ngx_open_and_stat_file(name->data, of, pool->log); if (rc == NGX_OK && !of->is_dir) { cln->handler = ngx_pool_cleanup_file; clnf = cln->data; clnf->fd = of->fd; clnf->name = name->data; clnf->log = pool->log; } return rc; } cln = ngx_pool_cleanup_add(pool, sizeof(ngx_open_file_cache_cleanup_t)); if (cln == NULL) { return NGX_ERROR; } now = ngx_time(); hash = ngx_crc32_long(name->data, name->len); file = ngx_open_file_lookup(cache, name, hash); if (file) { file->uses++; ngx_queue_remove(&file->queue); if (file->fd == NGX_INVALID_FILE && file->err == 0 && !file->is_dir) { /* file was not used often enough to keep open */ rc = ngx_open_and_stat_file(name->data, of, pool->log); if (rc != NGX_OK && (of->err == 0 || !of->errors)) { goto failed; } goto add_event; } if ((file->event && file->use_event) || (file->event == NULL && now - file->created < of->valid)) { if (file->err == 0) { of->fd = file->fd; of->uniq = file->uniq; of->mtime = file->mtime; of->size = file->size; of->is_dir = file->is_dir; of->is_file = file->is_file; of->is_link = file->is_link; of->is_exec = file->is_exec; if (!file->is_dir) { file->count++; ngx_open_file_add_event(cache, file, of, pool->log); } } else { of->err = file->err; } goto found; } ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0, "retest open file: %s, fd:%d, c:%d, e:%d", file->name, file->fd, file->count, file->err); if (file->is_dir) { /* * chances that directory became file are very small * so test_dir flag allows to use a single syscall * in ngx_file_info() instead of three syscalls */ of->test_dir = 1; } rc = ngx_open_and_stat_file(name->data, of, pool->log); if (rc != NGX_OK && (of->err == 0 || !of->errors)) { goto failed; } if (of->is_dir) { if (file->is_dir || file->err) { goto update; } /* file became directory */ } else if (of->err == 0) { /* file */ if (file->is_dir || file->err) { goto add_event; } if (of->uniq == file->uniq && of->mtime == file->mtime && of->size == file->size) { if (ngx_close_file(of->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, ngx_close_file_n " \"%s\" failed", name->data); } of->fd = file->fd; file->count++; if (file->event) { file->use_event = 1; goto renew; } ngx_open_file_add_event(cache, file, of, pool->log); goto renew; } /* file was changed */ } else { /* error to cache */ if (file->err || file->is_dir) { goto update; } /* file was removed, etc. */ } if (file->count == 0) { ngx_open_file_del_event(file); if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, ngx_close_file_n " \"%s\" failed", name->data); } goto add_event; } ngx_rbtree_delete(&cache->rbtree, &file->node); cache->current--; file->close = 1; goto create; } /* not found */ rc = ngx_open_and_stat_file(name->data, of, pool->log); if (rc != NGX_OK && (of->err == 0 || !of->errors)) { goto failed; } create: if (cache->current >= cache->max) { ngx_expire_old_cached_files(cache, 0, pool->log); } file = ngx_alloc(sizeof(ngx_cached_open_file_t), pool->log); if (file == NULL) { goto failed; } file->name = ngx_alloc(name->len + 1, pool->log); if (file->name == NULL) { ngx_free(file); file = NULL; goto failed; } ngx_cpystrn(file->name, name->data, name->len + 1); file->node.key = hash; ngx_rbtree_insert(&cache->rbtree, &file->node); cache->current++; file->uses = 1; file->count = 0; file->use_event = 0; file->event = NULL; add_event: ngx_open_file_add_event(cache, file, of, pool->log); update: file->fd = of->fd; file->err = of->err; if (of->err == 0) { file->uniq = of->uniq; file->mtime = of->mtime; file->size = of->size; file->close = 0; file->is_dir = of->is_dir; file->is_file = of->is_file; file->is_link = of->is_link; file->is_exec = of->is_exec; if (!of->is_dir) { file->count++; } } renew: file->created = now; found: file->accessed = now; ngx_queue_insert_head(&cache->expire_queue, &file->queue); ngx_log_debug5(NGX_LOG_DEBUG_CORE, pool->log, 0, "cached open file: %s, fd:%d, c:%d, e:%d, u:%d", file->name, file->fd, file->count, file->err, file->uses); if (of->err == 0) { if (!of->is_dir) { cln->handler = ngx_open_file_cleanup; ofcln = cln->data; ofcln->cache = cache; ofcln->file = file; ofcln->min_uses = of->min_uses; ofcln->log = pool->log; } return NGX_OK; } return NGX_ERROR; failed: if (file) { ngx_rbtree_delete(&cache->rbtree, &file->node); cache->current--; if (file->count == 0) { if (file->fd != NGX_INVALID_FILE) { if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, ngx_close_file_n " \"%s\" failed", file->name); } } ngx_free(file->name); ngx_free(file); } else { file->close = 1; } } if (of->fd != NGX_INVALID_FILE) { if (ngx_close_file(of->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, ngx_close_file_n " \"%s\" failed", name->data); } } return NGX_ERROR; }
/* NGX_OK NGX_AGAIN NGX_ERROR NGX_DECLINED: cache索引表中没有查找到,创建node并插入到红黑树和队列中,并且设置node节点到 c->node */ static ngx_int_t ngx_http_file_cache_exists(ngx_http_file_cache_t *cache, ngx_http_cache_t *c) { ngx_int_t rc; ngx_http_file_cache_node_t *fcn; // 对共享内存区上锁 ngx_shmtx_lock(&cache->shpool->mutex); // 在函数 ngx_init_zone_pool()中设置并创建 fcn = c->node; if (fcn == NULL) { // 在cache中查找key fcn = ngx_http_file_cache_lookup(cache, c->key); } // 在共享内存中找到cache if (fcn) { // 在队列中删除???? ngx_queue_remove(&fcn->queue); // ???? if (c->node == NULL) { fcn->uses++; fcn->count++; } // ???? if (fcn->error) { if (fcn->valid_sec < ngx_time()) { goto renew; } rc = NGX_OK; goto done; } if (fcn->exists || fcn->uses >= c->min_uses) { c->exists = fcn->exists; if (fcn->body_start) { c->body_start = fcn->body_start; } rc = NGX_OK; goto done; } rc = NGX_AGAIN; goto done; } //################ 在共享内存cache索引表中没有查找到此cache ################ // 1. 申请一个cache节点 fcn = ngx_slab_alloc_locked(cache->shpool, sizeof(ngx_http_file_cache_node_t)); if (fcn == NULL) { // 共享内存空间不足申请一个cache节点时,强制过期一个cache节点 ngx_shmtx_unlock(&cache->shpool->mutex); (void) ngx_http_file_cache_forced_expire(cache); ngx_shmtx_lock(&cache->shpool->mutex); fcn = ngx_slab_alloc_locked(cache->shpool, sizeof(ngx_http_file_cache_node_t)); if (fcn == NULL) { rc = NGX_ERROR; goto failed; } } // 2. 拷贝节点key ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t)); ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)], NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t)); // 3. 插入到红黑树中 ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node); fcn->uses = 1; fcn->count = 1; fcn->updating = 0; fcn->deleting = 0; renew: rc = NGX_DECLINED; fcn->valid_msec = 0; fcn->error = 0; fcn->exists = 0; fcn->valid_sec = 0; fcn->uniq = 0; fcn->body_start = 0; fcn->fs_size = 0; done: fcn->expire = ngx_time() + cache->inactive; ngx_queue_insert_head(&cache->sh->queue, &fcn->queue); c->uniq = fcn->uniq; c->error = fcn->error; c->node = fcn; // 设置node节点 failed: ngx_shmtx_unlock(&cache->shpool->mutex); return rc; }
void uv__write(uv_tcp_t* tcp) { uv_req_t* req; struct iovec* iov; int iovcnt; ssize_t n; uv_write_cb cb; assert(tcp->fd >= 0); /* TODO: should probably while(1) here until EAGAIN */ /* Get the request at the head of the queue. */ req = uv_write_queue_head(tcp); if (!req) { assert(tcp->write_queue_size == 0); uv__drain(tcp); return; } assert(req->handle == (uv_handle_t*)tcp); /* Cast to iovec. We had to have our own uv_buf_t instead of iovec * because Windows's WSABUF is not an iovec. */ assert(sizeof(uv_buf_t) == sizeof(struct iovec)); iov = (struct iovec*) &(req->bufs[req->write_index]); iovcnt = req->bufcnt - req->write_index; /* Now do the actual writev. Note that we've been updating the pointers * inside the iov each time we write. So there is no need to offset it. */ n = writev(tcp->fd, iov, iovcnt); cb = (uv_write_cb)req->cb; if (n < 0) { if (errno != EAGAIN) { uv_err_t err = uv_err_new((uv_handle_t*)tcp, errno); if (cb) { cb(req, -1); } return; } } else { /* Successful write */ /* The loop updates the counters. */ while (n > 0) { uv_buf_t* buf = &(req->bufs[req->write_index]); size_t len = buf->len; assert(req->write_index < req->bufcnt); if (n < len) { buf->base += n; buf->len -= n; tcp->write_queue_size -= n; n = 0; /* There is more to write. Break and ensure the watcher is pending. */ break; } else { /* Finished writing the buf at index req->write_index. */ req->write_index++; assert(n >= len); n -= len; assert(tcp->write_queue_size >= len); tcp->write_queue_size -= len; if (req->write_index == req->bufcnt) { /* Then we're done! */ assert(n == 0); /* Pop the req off tcp->write_queue. */ ngx_queue_remove(&req->queue); free(req->bufs); /* FIXME: we should not be allocing for each read */ req->bufs = NULL; /* NOTE: call callback AFTER freeing the request data. */ if (cb) { cb(req, 0); } if (!ngx_queue_empty(&tcp->write_queue)) { assert(tcp->write_queue_size > 0); } else { /* Write queue drained. */ uv__drain(tcp); } return; } } } } /* Either we've counted n down to zero or we've got EAGAIN. */ assert(n == 0 || n == -1); /* We're not done yet. */ assert(ev_is_active(&tcp->write_watcher)); ev_io_start(EV_DEFAULT_ &tcp->write_watcher); }
ngx_int_t ngx_http_connection_pool_get(ngx_peer_connection_t *pc, void *data) { u_char pc_addr[32] = {'\0'}; ngx_uint_t bucket_id, hash; ngx_queue_t *q, *cache, *free; ngx_connection_t *c; ngx_http_connection_pool_t *p; ngx_http_connection_pool_elt_t *item; p = data; #if (NGX_DEBUG) p->count--; #endif ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, "get keepalive peer"); p->failed = 0; hash = ngx_murmur_hash2((u_char *) pc->sockaddr, pc->socklen); bucket_id = hash % p->bucket_count; cache = &p->cache[bucket_id]; free = &p->free[bucket_id]; ngx_sprintf(pc_addr, "%s:%d", inet_ntoa(((struct sockaddr_in*)(pc->sockaddr))->sin_addr), ntohs(((struct sockaddr_in*)(pc->sockaddr))->sin_port)); for (q = ngx_queue_head(cache); q != ngx_queue_sentinel(cache); q = ngx_queue_next(q)) { item = ngx_queue_data(q, ngx_http_connection_pool_elt_t, queue); c = item->connection; if (ngx_memn2cmp((u_char *) &item->sockaddr, (u_char *) pc->sockaddr, item->socklen, pc->socklen) == 0) { ngx_queue_remove(q); ngx_queue_insert_head(free, q); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, "get keepalive peer: using connection %p", c); c->idle = 0; c->log = pc->log; c->read->log = pc->log; c->write->log = pc->log; c->pool->log = pc->log; pc->connection = c; pc->cached = 1; item->free = free; return NGX_DONE; } } return NGX_OK; }
static int ngx_http_lua_shdict_incr(lua_State *L) { int n; ngx_str_t key; uint32_t hash; ngx_int_t rc; ngx_http_lua_shdict_ctx_t *ctx; ngx_http_lua_shdict_node_t *sd; double num; u_char *p; ngx_shm_zone_t *zone; double value; n = lua_gettop(L); if (n != 3) { return luaL_error(L, "expecting 3 arguments, but only seen %d", n); } if (lua_type(L, 1) != LUA_TLIGHTUSERDATA) { return luaL_error(L, "bad \"zone\" argument"); } zone = lua_touserdata(L, 1); if (zone == NULL) { return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer"); } ctx = zone->data; if (lua_isnil(L, 2)) { lua_pushnil(L); lua_pushliteral(L, "nil key"); return 2; } key.data = (u_char *) luaL_checklstring(L, 2, &key.len); if (key.len == 0) { lua_pushnil(L); lua_pushliteral(L, "empty key"); return 2; } if (key.len > 65535) { lua_pushnil(L); lua_pushliteral(L, "key too long"); return 2; } hash = ngx_crc32_short(key.data, key.len); value = luaL_checknumber(L, 3); dd("looking up key %.*s in shared dict %.*s", (int) key.len, key.data, (int) ctx->name.len, ctx->name.data); ngx_shmtx_lock(&ctx->shpool->mutex); #if 1 ngx_http_lua_shdict_expire(ctx, 1); #endif rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd); dd("shdict lookup returned %d", (int) rc); if (rc == NGX_DECLINED || rc == NGX_DONE) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushnil(L); lua_pushliteral(L, "not found"); return 2; } /* rc == NGX_OK */ if (sd->value_type != LUA_TNUMBER || sd->value_len != sizeof(double)) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushnil(L); lua_pushliteral(L, "not a number"); return 2; } ngx_queue_remove(&sd->queue); ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); dd("setting value type to %d", (int) sd->value_type); p = sd->data + key.len; ngx_memcpy(&num, p, sizeof(double)); num += value; ngx_memcpy(p, (double *) &num, sizeof(double)); ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushnumber(L, num); lua_pushnil(L); return 2; }
/* On success returns NULL. On error returns a pointer to the write request * which had the error. */ static uv_write_t* uv__write(uv_tcp_t* tcp) { uv_write_t* req; struct iovec* iov; int iovcnt; ssize_t n; assert(tcp->fd >= 0); /* TODO: should probably while(1) here until EAGAIN */ /* Get the request at the head of the queue. */ req = uv_write_queue_head(tcp); if (!req) { assert(tcp->write_queue_size == 0); return NULL; } assert(req->handle == (uv_stream_t*)tcp); /* Cast to iovec. We had to have our own uv_buf_t instead of iovec * because Windows's WSABUF is not an iovec. */ assert(sizeof(uv_buf_t) == sizeof(struct iovec)); iov = (struct iovec*) &(req->bufs[req->write_index]); iovcnt = req->bufcnt - req->write_index; /* Now do the actual writev. Note that we've been updating the pointers * inside the iov each time we write. So there is no need to offset it. */ if (iovcnt == 1) { n = write(tcp->fd, iov[0].iov_base, iov[0].iov_len); } else { n = writev(tcp->fd, iov, iovcnt); } if (n < 0) { if (errno != EAGAIN) { /* Error */ uv_err_new((uv_handle_t*)tcp, errno); return req; } } else { /* Successful write */ /* Update the counters. */ while (n >= 0) { uv_buf_t* buf = &(req->bufs[req->write_index]); size_t len = buf->len; assert(req->write_index < req->bufcnt); if (n < len) { buf->base += n; buf->len -= n; tcp->write_queue_size -= n; n = 0; /* There is more to write. Break and ensure the watcher is pending. */ break; } else { /* Finished writing the buf at index req->write_index. */ req->write_index++; assert(n >= len); n -= len; assert(tcp->write_queue_size >= len); tcp->write_queue_size -= len; if (req->write_index == req->bufcnt) { /* Then we're done! */ assert(n == 0); /* Pop the req off tcp->write_queue. */ ngx_queue_remove(&req->queue); if (req->bufs != req->bufsml) { free(req->bufs); } req->bufs = NULL; /* Add it to the write_completed_queue where it will have its * callback called in the near future. * TODO: start trying to write the next request. */ ngx_queue_insert_tail(&tcp->write_completed_queue, &req->queue); ev_feed_event(EV_DEFAULT_ &tcp->write_watcher, EV_WRITE); return NULL; } } } } /* Either we've counted n down to zero or we've got EAGAIN. */ assert(n == 0 || n == -1); /* We're not done. */ ev_io_start(EV_DEFAULT_ &tcp->write_watcher); return NULL; }
int ngx_http_lua_ffi_shdict_store(ngx_shm_zone_t *zone, int op, u_char *key, size_t key_len, int value_type, u_char *str_value_buf, size_t str_value_len, double num_value, int exptime, int user_flags, char **errmsg, int *forcible) { int i, n; u_char c, *p; uint32_t hash; ngx_int_t rc; ngx_time_t *tp; ngx_rbtree_node_t *node; ngx_http_lua_shdict_ctx_t *ctx; ngx_http_lua_shdict_node_t *sd; if (zone == NULL) { return NGX_ERROR; } dd("exptime: %d", exptime); ctx = zone->data; *forcible = 0; hash = ngx_crc32_short(key, key_len); switch (value_type) { case LUA_TSTRING: /* do nothing */ break; case LUA_TNUMBER: dd("num value: %lf", num_value); str_value_buf = (u_char *) &num_value; str_value_len = sizeof(double); break; case LUA_TBOOLEAN: c = num_value ? 1 : 0; str_value_buf = &c; str_value_len = sizeof(u_char); break; case LUA_TNIL: if (op & (NGX_HTTP_LUA_SHDICT_ADD|NGX_HTTP_LUA_SHDICT_REPLACE)) { *errmsg = "attempt to add or replace nil values"; return NGX_ERROR; } str_value_buf = NULL; str_value_len = 0; break; default: *errmsg = "unsupported value type"; return NGX_ERROR; } ngx_shmtx_lock(&ctx->shpool->mutex); #if 1 ngx_http_lua_shdict_expire(ctx, 1); #endif rc = ngx_http_lua_shdict_lookup(zone, hash, key, key_len, &sd); dd("lookup returns %d", (int) rc); if (op & NGX_HTTP_LUA_SHDICT_REPLACE) { if (rc == NGX_DECLINED || rc == NGX_DONE) { ngx_shmtx_unlock(&ctx->shpool->mutex); *errmsg = "not found"; return NGX_DECLINED; } /* rc == NGX_OK */ goto replace; } if (op & NGX_HTTP_LUA_SHDICT_ADD) { if (rc == NGX_OK) { ngx_shmtx_unlock(&ctx->shpool->mutex); *errmsg = "exists"; return NGX_DECLINED; } if (rc == NGX_DONE) { /* exists but expired */ dd("go to replace"); goto replace; } /* rc == NGX_DECLINED */ dd("go to insert"); goto insert; } if (rc == NGX_OK || rc == NGX_DONE) { if (value_type == LUA_TNIL) { goto remove; } replace: if (str_value_buf && str_value_len == (size_t) sd->value_len) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "lua shared dict set: found old entry and value " "size matched, reusing it"); ngx_queue_remove(&sd->queue); ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); sd->key_len = (u_short) key_len; if (exptime > 0) { tp = ngx_timeofday(); sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + (uint64_t) exptime; } else { sd->expires = 0; } sd->user_flags = user_flags; sd->value_len = (uint32_t) str_value_len; dd("setting value type to %d", value_type); sd->value_type = (uint8_t) value_type; p = ngx_copy(sd->data, key, key_len); ngx_memcpy(p, str_value_buf, str_value_len); ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_OK; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "lua shared dict set: found old entry but value size " "NOT matched, removing it first"); remove: ngx_queue_remove(&sd->queue); node = (ngx_rbtree_node_t *) ((u_char *) sd - offsetof(ngx_rbtree_node_t, color)); ngx_rbtree_delete(&ctx->sh->rbtree, node); ngx_slab_free_locked(ctx->shpool, node); } insert: /* rc == NGX_DECLINED or value size unmatch */ if (str_value_buf == NULL) { ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_OK; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "lua shared dict set: creating a new entry"); n = offsetof(ngx_rbtree_node_t, color) + offsetof(ngx_http_lua_shdict_node_t, data) + key_len + str_value_len; node = ngx_slab_alloc_locked(ctx->shpool, n); if (node == NULL) { if (op & NGX_HTTP_LUA_SHDICT_SAFE_STORE) { ngx_shmtx_unlock(&ctx->shpool->mutex); *errmsg = "no memory"; return NGX_ERROR; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "lua shared dict set: overriding non-expired items " "due to memory shortage for entry \"%*s\"", key_len, key); for (i = 0; i < 30; i++) { if (ngx_http_lua_shdict_expire(ctx, 0) == 0) { break; } *forcible = 1; node = ngx_slab_alloc_locked(ctx->shpool, n); if (node != NULL) { goto allocated; } } ngx_shmtx_unlock(&ctx->shpool->mutex); *errmsg = "no memory"; return NGX_ERROR; } allocated: sd = (ngx_http_lua_shdict_node_t *) &node->color; node->key = hash; sd->key_len = (u_short) key_len; if (exptime > 0) { tp = ngx_timeofday(); sd->expires = (uint64_t) tp->sec * 1000 + tp->msec + (uint64_t) exptime; } else { sd->expires = 0; } sd->user_flags = user_flags; sd->value_len = (uint32_t) str_value_len; dd("setting value type to %d", value_type); sd->value_type = (uint8_t) value_type; p = ngx_copy(sd->data, key, key_len); ngx_memcpy(p, str_value_buf, str_value_len); ngx_rbtree_insert(&ctx->sh->rbtree, node); ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); ngx_shmtx_unlock(&ctx->shpool->mutex); return NGX_OK; }
static void ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last, ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan, ngx_uint_t ans) { char *err; u_char *cname; size_t len; int32_t ttl; uint32_t hash; in_addr_t addr, *addrs; ngx_str_t name; ngx_uint_t qtype, qident, naddrs, a, i, n, start; ngx_resolver_an_t *an; ngx_resolver_ctx_t *ctx, *next; ngx_resolver_node_t *rn; if (ngx_resolver_copy(r, &name, buf, &buf[12], &buf[last]) != NGX_OK) { return; } ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver qs:%V", &name); hash = ngx_crc32_short(name.data, name.len); /* lock name mutex */ rn = ngx_resolver_lookup_name(r, &name, hash); if (rn == NULL || rn->query == NULL) { ngx_log_error(r->log_level, r->log, 0, "unexpected response for %V", &name); goto failed; } qident = (rn->query[0] << 8) + rn->query[1]; if (ident != qident) { ngx_log_error(r->log_level, r->log, 0, "wrong ident %ui response for %V, expect %ui", ident, &name, qident); goto failed; } ngx_resolver_free(r, name.data); if (code == 0 && nan == 0) { code = 3; /* NXDOMAIN */ } if (code) { next = rn->waiting; rn->waiting = NULL; ngx_queue_remove(&rn->queue); ngx_rbtree_delete(&r->name_rbtree, &rn->node); ngx_resolver_free_node(r, rn); /* unlock name mutex */ while (next) { ctx = next; ctx->state = code; next = ctx->next; ctx->handler(ctx); } return; } i = ans; naddrs = 0; addr = 0; addrs = NULL; cname = NULL; qtype = 0; ttl = 0; for (a = 0; a < nan; a++) { start = i; while (i < last) { if (buf[i] & 0xc0) { i += 2; goto found; } if (buf[i] == 0) { i++; goto test_length; } i += 1 + buf[i]; } goto short_response; test_length: if (i - start < 2) { err = "invalid name in dns response"; goto invalid; } found: if (i + sizeof(ngx_resolver_an_t) >= last) { goto short_response; } an = (ngx_resolver_an_t *) &buf[i]; qtype = (an->type_hi << 8) + an->type_lo; len = (an->len_hi << 8) + an->len_lo; ttl = (an->ttl[0] << 24) + (an->ttl[1] << 16) + (an->ttl[2] << 8) + (an->ttl[3]); if (ttl < 0) { ttl = 0; } if (qtype == NGX_RESOLVE_A) { i += sizeof(ngx_resolver_an_t); if (i + len > last) { goto short_response; } addr = htonl((buf[i] << 24) + (buf[i + 1] << 16) + (buf[i + 2] << 8) + (buf[i + 3])); naddrs++; i += len; } else if (qtype == NGX_RESOLVE_CNAME) { cname = &buf[i] + sizeof(ngx_resolver_an_t); i += sizeof(ngx_resolver_an_t) + len; } else if (qtype == NGX_RESOLVE_DNAME) { i += sizeof(ngx_resolver_an_t) + len; } else { ngx_log_error(r->log_level, r->log, 0, "unexpected qtype %ui", qtype); } } ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver naddrs:%ui cname:%p ttl:%d", naddrs, cname, ttl); if (naddrs) { if (naddrs == 1) { rn->u.addr = addr; } else { addrs = ngx_resolver_alloc(r, naddrs * sizeof(in_addr_t)); if (addrs == NULL) { return; } n = 0; i = ans; for (a = 0; a < nan; a++) { for ( ;; ) { if (buf[i] & 0xc0) { i += 2; goto ok; } if (buf[i] == 0) { i++; goto ok; } i += 1 + buf[i]; } ok: an = (ngx_resolver_an_t *) &buf[i]; qtype = (an->type_hi << 8) + an->type_lo; len = (an->len_hi << 8) + an->len_lo; i += sizeof(ngx_resolver_an_t); if (qtype == NGX_RESOLVE_A) { addrs[n++] = htonl((buf[i] << 24) + (buf[i + 1] << 16) + (buf[i + 2] << 8) + (buf[i + 3])); if (n == naddrs) { break; } } i += len; } rn->u.addrs = addrs; addrs = ngx_resolver_dup(r, rn->u.addrs, naddrs * sizeof(in_addr_t)); if (addrs == NULL) { return; } } rn->naddrs = (u_short) naddrs; ngx_queue_remove(&rn->queue); rn->valid = ngx_time() + (r->valid ? r->valid : ttl); rn->expire = ngx_time() + r->expire; ngx_queue_insert_head(&r->name_expire_queue, &rn->queue); next = rn->waiting; rn->waiting = NULL; /* unlock name mutex */ while (next) { ctx = next; ctx->state = NGX_OK; ctx->naddrs = naddrs; ctx->addrs = (naddrs == 1) ? &ctx->addr : addrs; ctx->addr = addr; next = ctx->next; ctx->handler(ctx); } if (naddrs > 1) { ngx_resolver_free(r, addrs); } return; } else if (cname) { /* CNAME only */ if (ngx_resolver_copy(r, &name, buf, cname, &buf[last]) != NGX_OK) { return; } ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver cname:\"%V\"", &name); ngx_queue_remove(&rn->queue); rn->cnlen = (u_short) name.len; rn->u.cname = name.data; rn->valid = ngx_time() + (r->valid ? r->valid : ttl); rn->expire = ngx_time() + r->expire; ngx_queue_insert_head(&r->name_expire_queue, &rn->queue); ctx = rn->waiting; rn->waiting = NULL; if (ctx) { ctx->name = name; (void) ngx_resolve_name_locked(r, ctx); } return; } ngx_log_error(r->log_level, r->log, 0, "no A or CNAME types in DNS responses, unknown query type: %ui", qtype); return; short_response: err = "short dns response"; invalid: /* unlock name mutex */ ngx_log_error(r->log_level, r->log, 0, err); return; failed: /* unlock name mutex */ ngx_resolver_free(r, name.data); return; }
static ngx_int_t ngx_http_lua_shdict_lookup(ngx_shm_zone_t *shm_zone, ngx_uint_t hash, u_char *kdata, size_t klen, ngx_http_lua_shdict_node_t **sdp) { ngx_int_t rc; ngx_time_t *tp; uint64_t now; int64_t ms; ngx_rbtree_node_t *node, *sentinel; ngx_http_lua_shdict_ctx_t *ctx; ngx_http_lua_shdict_node_t *sd; ctx = shm_zone->data; node = ctx->sh->rbtree.root; sentinel = ctx->sh->rbtree.sentinel; while (node != sentinel) { if (hash < node->key) { node = node->left; continue; } if (hash > node->key) { node = node->right; continue; } /* hash == node->key */ sd = (ngx_http_lua_shdict_node_t *) &node->color; rc = ngx_memn2cmp(kdata, sd->data, klen, (size_t) sd->key_len); if (rc == 0) { ngx_queue_remove(&sd->queue); ngx_queue_insert_head(&ctx->sh->queue, &sd->queue); *sdp = sd; dd("node expires: %lld", (long long) sd->expires); if (sd->expires != 0) { tp = ngx_timeofday(); now = (uint64_t) tp->sec * 1000 + tp->msec; ms = sd->expires - now; dd("time to live: %lld", (long long) ms); if (ms < 0) { dd("node already expired"); return NGX_DONE; } } return NGX_OK; } node = (rc < 0) ? node->left : node->right; } *sdp = NULL; return NGX_DECLINED; }
ngx_int_t ngx_resolve_addr(ngx_resolver_ctx_t *ctx) { u_char *name; ngx_resolver_t *r; ngx_resolver_node_t *rn; r = ctx->resolver; ctx->addr = ntohl(ctx->addr); /* lock addr mutex */ rn = ngx_resolver_lookup_addr(r, ctx->addr); if (rn) { if (rn->valid >= ngx_time()) { ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolve cached"); ngx_queue_remove(&rn->queue); rn->expire = ngx_time() + r->expire; ngx_queue_insert_head(&r->addr_expire_queue, &rn->queue); name = ngx_resolver_dup(r, rn->name, rn->nlen); if (name == NULL) { goto failed; } ctx->name.len = rn->nlen; ctx->name.data = name; /* unlock addr mutex */ ctx->state = NGX_OK; ctx->handler(ctx); ngx_resolver_free(r, name); return NGX_OK; } if (rn->waiting) { ctx->next = rn->waiting; rn->waiting = ctx; ctx->state = NGX_AGAIN; /* unlock addr mutex */ return NGX_OK; } ngx_queue_remove(&rn->queue); ngx_resolver_free(r, rn->query); rn->query = NULL; } else { rn = ngx_resolver_alloc(r, sizeof(ngx_resolver_node_t)); if (rn == NULL) { goto failed; } rn->node.key = ctx->addr; rn->query = NULL; ngx_rbtree_insert(&r->addr_rbtree, &rn->node); } if (ngx_resolver_create_addr_query(rn, ctx) != NGX_OK) { goto failed; } if (ngx_resolver_send_query(r, rn) != NGX_OK) { goto failed; } ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t)); if (ctx->event == NULL) { goto failed; } ctx->event->handler = ngx_resolver_timeout_handler; ctx->event->data = ctx; ctx->event->log = r->log; ctx->ident = -1; ngx_add_timer(ctx->event, ctx->timeout); if (ngx_queue_empty(&r->addr_resend_queue)) { ngx_add_timer(r->event, (ngx_msec_t) (r->resend_timeout * 1000)); } rn->expire = ngx_time() + r->resend_timeout; ngx_queue_insert_head(&r->addr_resend_queue, &rn->queue); rn->cnlen = 0; rn->naddrs = 0; rn->name = NULL; rn->nlen = 0; rn->valid = 0; rn->waiting = ctx; /* unlock addr mutex */ ctx->state = NGX_AGAIN; return NGX_OK; failed: if (rn) { ngx_rbtree_delete(&r->addr_rbtree, &rn->node); if (rn->query) { ngx_resolver_free(r, rn->query); } ngx_resolver_free(r, rn); } /* unlock addr mutex */ if (ctx->event) { ngx_resolver_free(r, ctx->event); } ngx_resolver_free(r, ctx); return NGX_ERROR; }
static int ngx_http_lua_shdict_flush_expired(lua_State *L) { ngx_queue_t *q, *prev; ngx_http_lua_shdict_node_t *sd; ngx_http_lua_shdict_ctx_t *ctx; ngx_shm_zone_t *zone; ngx_time_t *tp; int freed = 0; int attempts = 0; ngx_rbtree_node_t *node; uint64_t now; int n; n = lua_gettop(L); if (n != 1 && n != 2) { return luaL_error(L, "expecting 1 or 2 argument(s), but saw %d", n); } luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); zone = lua_touserdata(L, 1); if (zone == NULL) { return luaL_error(L, "bad user data for the ngx_shm_zone_t pointer"); } if (n == 2) { attempts = luaL_checkint(L, 2); } ctx = zone->data; ngx_shmtx_lock(&ctx->shpool->mutex); if (ngx_queue_empty(&ctx->sh->queue)) { ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushnumber(L, 0); return 1; } tp = ngx_timeofday(); now = (uint64_t) tp->sec * 1000 + tp->msec; q = ngx_queue_last(&ctx->sh->queue); while (q != ngx_queue_sentinel(&ctx->sh->queue)) { prev = ngx_queue_prev(q); sd = ngx_queue_data(q, ngx_http_lua_shdict_node_t, queue); if (sd->expires != 0 && sd->expires <= now) { ngx_queue_remove(q); node = (ngx_rbtree_node_t *) ((u_char *) sd - offsetof(ngx_rbtree_node_t, color)); ngx_rbtree_delete(&ctx->sh->rbtree, node); ngx_slab_free_locked(ctx->shpool, node); freed++; if (attempts && freed == attempts) { break; } } q = prev; } ngx_shmtx_unlock(&ctx->shpool->mutex); lua_pushnumber(L, freed); return 1; }
static ngx_int_t ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data) { time_t now; uint32_t index, index1, index2; uint32_t diff1, diff2; ngx_queue_t *q, *temp; ngx_segment_node_t node, *p; ngx_http_upstream_rr_peer_t *peer; ngx_http_upstream_chash_server_t *server; ngx_http_upstream_chash_srv_conf_t *ucscf; ngx_http_upstream_chash_peer_data_t *uchpd = data; ngx_http_upstream_chash_down_server_t *down_server; ucscf = uchpd->ucscf; if (!ngx_queue_empty(&ucscf->down_servers)) { q = ngx_queue_head(&ucscf->down_servers); while(q != ngx_queue_sentinel(&ucscf->down_servers)) { temp = ngx_queue_next(q); down_server = ngx_queue_data(q, ngx_http_upstream_chash_down_server_t, queue); now = ngx_time(); if (now >= down_server->timeout) { peer = ucscf->servers[down_server->id].peer; #if (NGX_HTTP_UPSTREAM_CHECK) if (!ngx_http_upstream_check_peer_down(peer->check_index)) { #endif peer->fails = 0; peer->down = 0; ucscf->servers[down_server->id].down = 0; ngx_queue_remove(&down_server->queue); node.key = down_server->id; ucscf->tree->insert(ucscf->tree, 1, 1, ucscf->number, down_server->id, &node); #if (NGX_HTTP_UPSTREAM_CHECK) } #endif } q = temp; } } pc->cached = 0; pc->connection = NULL; index = ngx_http_upstream_chash_get_server_index(ucscf->servers, ucscf->number, uchpd->hash); server = &ucscf->servers[index]; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, "consistent hash [peer name]:%V %ud", &server->peer->name, server->hash); if ( #if (NGX_HTTP_UPSTREAM_CHECK) ngx_http_upstream_check_peer_down(server->peer->check_index) || #endif server->peer->fails > server->peer->max_fails || server->peer->down ) { ngx_http_upstream_chash_delete_node(ucscf, server); while (1) { p = ucscf->tree->query(ucscf->tree, 1, 1, ucscf->number, 1, index - 1); index1 = p->key; p = ucscf->tree->query(ucscf->tree, 1, 1, ucscf->number, index + 1, ucscf->number); index2 = p->key; if (index1 == ucscf->tree->extreme) { if (index2 == ucscf->tree->extreme) { ngx_log_error(NGX_LOG_ERR, pc->log, 0, "all servers are down!"); return NGX_BUSY; } else { index1 = index2; server = &ucscf->servers[index2]; } } else if (index2 == ucscf->tree->extreme) { server = &ucscf->servers[index1]; } else { if (ucscf->servers[index1].hash > uchpd->hash) { diff1 = ucscf->servers[index1].hash - uchpd->hash; } else { diff1 = uchpd->hash - ucscf->servers[index1].hash; } if (uchpd->hash > ucscf->servers[index2].hash) { diff2 = uchpd->hash - ucscf->servers[index2].hash; } else { diff2 = ucscf->servers[index2].hash - uchpd->hash; } index1 = diff1 > diff2 ? index2 : index1; server = &ucscf->servers[index1]; } if ( #if (NGX_HTTP_UPSTREAM_CHECK) ngx_http_upstream_check_peer_down(server->peer->check_index) || #endif server->peer->fails > server->peer->max_fails || server->peer->down) { ngx_http_upstream_chash_delete_node(ucscf, server); } else { break; } index = index1; } } if (server->down) { ngx_log_error(NGX_LOG_ERR, pc->log, 0, "all servers are down"); return NGX_BUSY; } uchpd->server = server; peer = server->peer; pc->name = &peer->name; pc->sockaddr = peer->sockaddr; pc->socklen = peer->socklen; return NGX_OK; }
static time_t ngx_http_file_cache_expire(ngx_http_file_cache_t *cache) { u_char *name, *p; size_t len; time_t now, wait; ngx_path_t *path; ngx_queue_t *q; ngx_http_file_cache_node_t *fcn; u_char key[2 * NGX_HTTP_CACHE_KEY_LEN]; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "http file cache expire"); path = cache->path; len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN; name = ngx_alloc(len + 1, ngx_cycle->log); if (name == NULL) { return 10; } ngx_memcpy(name, path->name.data, path->name.len); now = ngx_time(); ngx_shmtx_lock(&cache->shpool->mutex); for ( ;; ) { if (ngx_queue_empty(&cache->sh->queue)) { wait = 10; break; } q = ngx_queue_last(&cache->sh->queue); fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue); wait = fcn->expire - now; if (wait > 0) { wait = wait > 10 ? 10 : wait; break; } ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "http file cache expire: #%d %d %02xd%02xd%02xd%02xd", fcn->count, fcn->exists, fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]); if (fcn->count == 0) { ngx_http_file_cache_delete(cache, q, name); continue; } if (fcn->deleting) { continue; } p = ngx_hex_dump(key, (u_char *) &fcn->node.key, sizeof(ngx_rbtree_key_t)); len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t); (void) ngx_hex_dump(p, fcn->key, len); /* * abnormally exited workers may leave locked cache entries, * and although it may be safe to remove them completely, * we prefer to remove them from inactive queue and rbtree * only, and to allow other leaks */ ngx_queue_remove(q); ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node); ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "ignore long locked inactive cache entry %*s, count:%d", 2 * NGX_HTTP_CACHE_KEY_LEN, key, fcn->count); } ngx_shmtx_unlock(&cache->shpool->mutex); ngx_free(name); return wait; }