int acl_fiber_sem_wait(ACL_FIBER_SEM *sem) { ACL_FIBER *curr; if (sem->tid != acl_pthread_self()) return -1; if (sem->num > 0) { sem->num--; return sem->num; } curr = acl_fiber_running(); if (curr == NULL) return -1; acl_ring_prepend(&sem->waiting, &curr->me); acl_fiber_switch(); /* if switch to me because other killed me, I should detach myself; * else if because other unlock, I'll be detached twice which is * hamless because ACL_RING can deal with it. */ acl_ring_detach(&curr->me); return sem->num; }
void acl_fiber_ready(ACL_FIBER *fiber) { if (fiber->status != FIBER_STATUS_EXITING) { fiber->status = FIBER_STATUS_READY; acl_ring_prepend(&__thread_fiber->ready, &fiber->me); } }
static void test(void) { ACL_RING ring; acl_ring_init(&ring); for (int i = 0; i < 10; i++) { A* a = new A; a->m = i; acl_ring_prepend(&ring, &a->entry); printf("put %d\r\n", i); } printf("-------------------------------------------------------\r\n"); while (true) { ACL_RING* head = acl_ring_pop_head(&ring); if (head == NULL) break; A* a = ACL_RING_TO_APPL(head, A, entry); printf("pop %d\r\n", a->m); delete a; } exit (0); }
static int __lock(ACL_FIBER_MUTEX *lk, int block) { ACL_FIBER *curr = acl_fiber_running(); if (lk->owner == NULL) { lk->owner = acl_fiber_running(); acl_ring_prepend(&curr->holding, &lk->me); return 0; } // xxx: no support recursion lock assert(lk->owner != curr); if (!block) return -1; acl_ring_prepend(&lk->waiting, &curr->me); curr->waiting = lk; acl_fiber_switch(); /* if switch to me because other killed me, I should detach myself; * else if because other unlock, I'll be detached twice which is * hamless because ACL_RING can deal with it. */ acl_ring_detach(&curr->me); if (lk->owner == curr) return 0; if (acl_fiber_killed(curr)) acl_msg_info("%s(%d), %s: lock fiber-%u was killed", __FILE__, __LINE__, __FUNCTION__, acl_fiber_id(curr)); else acl_msg_warn("%s(%d), %s: qlock: owner=%p self=%p oops", __FILE__, __LINE__, __FUNCTION__, lk->owner, curr); return 0; }
static void __create_ring(int flag, int size) { MY_TYPE *my_type; int i; for (i = 0; i < size; i++) { my_type = acl_mycalloc(1, sizeof(MY_TYPE)); snprintf(my_type->name, sizeof(my_type->name), "name:%d", i); snprintf(my_type->value, sizeof(my_type->value), "value:%d", i); my_type->i = i; if (flag) acl_ring_prepend(&__ring_header, &my_type->ring_entry); else acl_ring_append(&__ring_header, &my_type->ring_entry); } }
static void fiber_swap(ACL_FIBER *from, ACL_FIBER *to) { if (from->status == FIBER_STATUS_EXITING) { size_t slot = from->slot; int n = acl_ring_size(&__thread_fiber->dead); /* if the cached dead fibers reached the limit, * some will be freed */ if (n > MAX_CACHE) { n -= MAX_CACHE; fiber_kick(n); } if (!from->sys) __thread_fiber->count--; __thread_fiber->fibers[slot] = __thread_fiber->fibers[--__thread_fiber->slot]; __thread_fiber->fibers[slot]->slot = slot; acl_ring_prepend(&__thread_fiber->dead, &from->me); } #ifdef USE_JMP /* use setcontext() for the initial jump, as it allows us to set up * a stack, but continue with longjmp() as it's much faster. */ if (SETJMP(from->env) == 0) { /* context just be used once for set up a stack, which will * be freed in fiber_start. The context in __thread_fiber * was set NULL. */ if (to->context != NULL) setcontext(to->context); else LONGJMP(to->env); } #else if (swapcontext(from->context, to->context) < 0) acl_msg_fatal("%s(%d), %s: swapcontext error %s", __FILE__, __LINE__, __FUNCTION__, acl_last_serror()); #endif }
static int __wlock(ACL_FIBER_RWLOCK *lk, int block) { ACL_FIBER *curr; if (lk->writer == NULL && lk->readers == 0) { lk->writer = acl_fiber_running(); return 1; } if (!block) return 0; curr = acl_fiber_running(); acl_ring_prepend(&lk->wwaiting, &curr->me); acl_fiber_switch(); /* if switch to me because other killed me, I should detach myself */ acl_ring_detach(&curr->me); return 1; }
static void event_loop(ACL_EVENT *eventp) { const char *myname = "event_loop"; EVENT_SELECT_THR *event_thr = (EVENT_SELECT_THR *) eventp; ACL_EVENT_NOTIFY_FN worker_fn; void *worker_arg; ACL_SOCKET sockfd; ACL_EVENT_TIMER *timer; int select_delay, nready, i; ACL_EVENT_FDTABLE *fdp; ACL_RING timer_ring, *entry_ptr; struct timeval tv, *tvp; fd_set rmask; /* enabled read events */ fd_set wmask; /* enabled write events */ fd_set xmask; /* for bad news mostly */ acl_ring_init(&timer_ring); SET_TIME(eventp->event_present); THREAD_LOCK(&event_thr->event.tm_mutex); /* * Find out when the next timer would go off. Timer requests are sorted. * If any timer is scheduled, adjust the delay appropriately. */ if ((timer = ACL_FIRST_TIMER(&eventp->timer_head)) != 0) { select_delay = (int) (timer->when - eventp->event_present + 1000000 - 1) / 1000000; if (select_delay < 0) { select_delay = 0; } else if (eventp->delay_sec >= 0 && select_delay > eventp->delay_sec) { select_delay = eventp->delay_sec; } } else { select_delay = eventp->delay_sec; } THREAD_UNLOCK(&event_thr->event.tm_mutex); THREAD_LOCK(&event_thr->event.tb_mutex); if (event_thr_prepare(eventp) == 0) { if (eventp->fdcnt_ready == 0) { if (select_delay <= 0) select_delay = 1; sleep(select_delay); } THREAD_UNLOCK(&event_thr->event.tb_mutex); goto TAG_DONE; } if (eventp->fdcnt_ready > 0) { tv.tv_sec = 0; tv.tv_usec = 0; tvp = &tv; } else if (select_delay < 0) { tvp = NULL; } else { tv.tv_sec = select_delay; tv.tv_usec = eventp->delay_usec; tvp = &tv; } rmask = event_thr->rmask; wmask = event_thr->wmask; xmask = event_thr->xmask; THREAD_UNLOCK(&event_thr->event.tb_mutex); event_thr->event.blocked = 1; nready = select(eventp->maxfd + 1, &rmask, &wmask, &xmask, tvp); event_thr->event.blocked = 0; if (nready < 0) { if (acl_last_error() != ACL_EINTR) { char ebuf[256]; acl_msg_fatal("%s(%d), %s: event_loop: select: %s", __FILE__, __LINE__, myname, acl_last_strerror(ebuf, sizeof(ebuf))); } goto TAG_DONE; } else if (nready == 0) goto TAG_DONE; THREAD_LOCK(&event_thr->event.tb_mutex); for (i = 0; i < eventp->fdcnt; i++) { fdp = eventp->fdtabs[i]; /* if fdp has been set in eventp->fdtabs_ready ? */ if ((fdp->event_type & (ACL_EVENT_XCPT | ACL_EVENT_RW_TIMEOUT))) continue; sockfd = ACL_VSTREAM_SOCK(fdp->stream); if (FD_ISSET(sockfd, &xmask)) { fdp->event_type |= ACL_EVENT_XCPT; fdp->fdidx_ready = eventp->fdcnt_ready; eventp->fdtabs_ready[eventp->fdcnt_ready++] = fdp; continue; } if (FD_ISSET(sockfd, &rmask)) { fdp->stream->sys_read_ready = 1; /* has been set in fdtabs_ready ? */ if ((fdp->event_type & ACL_EVENT_READ) == 0) { fdp->event_type |= ACL_EVENT_READ; fdp->fdidx_ready = eventp->fdcnt_ready; eventp->fdtabs_ready[eventp->fdcnt_ready++] = fdp; } } else if (fdp->w_callback && FD_ISSET(sockfd, &wmask)) { fdp->event_type |= ACL_EVENT_WRITE; fdp->fdidx_ready = eventp->fdcnt_ready; eventp->fdtabs_ready[eventp->fdcnt_ready++] = fdp; } } THREAD_UNLOCK(&event_thr->event.tb_mutex); TAG_DONE: /* * Deliver timer events. Requests are sorted: we can stop when we reach * the future or the list end. Allow the application to update the timer * queue while it is being called back. To this end, we repeatedly pop * the first request off the timer queue before delivering the event to * the application. */ SET_TIME(eventp->event_present); THREAD_LOCK(&event_thr->event.tm_mutex); while ((timer = ACL_FIRST_TIMER(&eventp->timer_head)) != 0) { if (timer->when > eventp->event_present) break; acl_ring_detach(&timer->ring); /* first this */ acl_ring_prepend(&timer_ring, &timer->ring); } THREAD_UNLOCK(&event_thr->event.tm_mutex); while (1) { entry_ptr = acl_ring_pop_head(&timer_ring); if (entry_ptr == NULL) break; timer = ACL_RING_TO_TIMER(entry_ptr); worker_fn = timer->callback; worker_arg = timer->context; worker_fn(ACL_EVENT_TIME, worker_arg); acl_myfree(timer); } event_thr_fire(eventp); }
void acl_xml_node_add_child(ACL_XML_NODE *parent, ACL_XML_NODE *child) { acl_ring_prepend(&parent->children, &child->node); child->parent = parent; }
/* 查询DNS信息,采用外挂模块的方式 */ static void thrpool_nslookup(SERVICE *service, CLIENT_ENTRY *entry, const char *domain, int port) { const char *myname = "thrpool_nslookup"; DNS_CTX dns_ctx; DNS_RING *list; char *ptr; entry->tm.stamp = time(NULL); memset(&dns_ctx, 0, sizeof(dns_ctx)); dns_ctx.begin = entry->tm.stamp; STRNCPY(dns_ctx.domain_key, domain, sizeof(dns_ctx.domain_key)); ptr = strchr(dns_ctx.domain_key, ':'); /* 仅留下域名部分 */ if (ptr) *ptr = 0; entry->server_port = port; if (entry->server_port <= 0) entry->server_port = 80; /* 将域名字符串都转换成小写,以便于进行哈希查询 */ acl_lowercase(dns_ctx.domain_key); dns_ctx.context = service; dns_ctx.callback = thrpool_nslookup_complete; STRNCPY(entry->domain_key, dns_ctx.domain_key, sizeof(entry->domain_key)); /* 先查询DNS查询表中是否已经包含本次需要被查询的域名 */ list = (DNS_RING *) acl_htable_find(service->dns_table, dns_ctx.domain_key); if (list) { /* 将本次对同一域名的查询添加进同一个查询链中 */ acl_ring_prepend(&list->ring, &entry->dns_entry); /* 将查询链对象的引用计数加1 */ list->nrefer++; /* 如果该查询链已经存在,说明有查询任务等待返回,其返回后会一同将 * 本次任务进行触发,如果此处触发新任务,则会造成内存访问冲突,因为 * 查询DNS的过程是由一组线程池进行查询的。 * (void) dns_server_lookup(proxy_entry->aio_proxy->dns_server, &dns_ctx); */ return; } /* 创建一个新的查询链对象,并将本次查询任务加入该查询链中及将该查询链加入查询表中 */ list = (DNS_RING *) acl_mycalloc(1, sizeof(DNS_RING)); acl_ring_init(&list->ring); STRNCPY(list->domain_key, dns_ctx.domain_key, sizeof(list->domain_key)); /* 将本次查询任务加入新的查询链中且将查询链的引用计数加1 */ acl_ring_prepend(&list->ring, &entry->dns_entry); list->nrefer++; /* 将新的查询链加入查询表中 */ if (acl_htable_enter(service->dns_table, list->domain_key, (char *) list) == NULL) acl_msg_fatal("%s: add domain(%s) to table error", myname, list->domain_key); /* 开始启动DNS查询过程 */ (void) dns_server_lookup(service->dns_server, &dns_ctx); }