static void connection_handler(IOCHAN iochan, int event) { struct connection *co = iochan_getdata(iochan); struct client *cl; struct host *host = co->host; yaz_mutex_enter(host->mutex); cl = co->client; if (!cl) { /* no client associated with it.. We are probably getting a closed connection from the target.. Or, perhaps, an unexpected package.. We will just close the connection */ yaz_log(YLOG_LOG, "timeout connection %p event=%d", co, event); remove_connection_from_host(co); yaz_mutex_leave(host->mutex); connection_destroy(co); } else if (event & EVENT_TIMEOUT) { if (co->state == Conn_Connecting) { yaz_log(YLOG_WARN, "%p connect timeout %s", co, client_get_id(cl)); client_set_state(cl, Client_Error); remove_connection_from_host(co); yaz_mutex_leave(host->mutex); connection_destroy(co); } else { yaz_log(YLOG_LOG, "%p Connection idle timeout %s", co, client_get_id(cl)); remove_connection_from_host(co); yaz_mutex_leave(host->mutex); connection_destroy(co); } } else { yaz_mutex_leave(host->mutex); client_lock(cl); non_block_events(co); ZOOM_connection_fire_event_socket(co->link, event); non_block_events(co); client_unlock(cl); if (co->link) { iochan_setflags(iochan, ZOOM_connection_get_mask(co->link)); iochan_setfd(iochan, ZOOM_connection_get_socket(co->link)); } } }
void http_server_incref(http_server_t hs) { assert(hs); yaz_mutex_enter(hs->mutex); (hs->ref_count)++; yaz_mutex_leave(hs->mutex); }
normalize_record_t normalize_cache_get(normalize_cache_t nc, struct conf_service *service, const char *spec) { normalize_record_t nt; struct cached_item *ci; yaz_mutex_enter(nc->mutex); for (ci = nc->items; ci; ci = ci->next) if (!strcmp(spec, ci->spec)) break; if (ci) nt = ci->nt; else { nt = normalize_record_create(service, spec); if (nt) { ci = nmem_malloc(nc->nmem, sizeof(*ci)); ci->next = nc->items; nc->items = ci; ci->nt = nt; ci->spec = nmem_strdup(nc->nmem, spec); } } yaz_mutex_leave(nc->mutex); return nt; }
// Creates a new connection for client, associated with the host of // client's database static struct connection *connection_create(struct client *cl, struct host *host, int operation_timeout, int session_timeout, iochan_man_t iochan_man) { struct connection *co; co = xmalloc(sizeof(*co)); co->host = host; co->client = cl; co->zproxy = 0; client_set_connection(cl, co); co->link = 0; co->state = Conn_Closed; co->operation_timeout = operation_timeout; co->session_timeout = session_timeout; if (host->ipport) connection_connect(co, iochan_man); yaz_mutex_enter(host->mutex); co->next = co->host->connections; co->host->connections = co; yaz_mutex_leave(host->mutex); connection_use(1); return co; }
static struct http_session *locate_session(struct http_channel *c) { struct http_request *rq = c->request; struct http_response *rs = c->response; struct http_session *p; const char *session = http_argbyname(rq, "session"); http_sessions_t http_sessions = c->http_sessions; unsigned int id; if (!session) { error(rs, PAZPAR2_MISSING_PARAMETER, "session"); return 0; } id = atoi(session); yaz_mutex_enter(http_sessions->mutex); for (p = http_sessions->session_list; p; p = p->next) if (id == p->session_id) break; if (p) p->activity_counter++; yaz_mutex_leave(http_sessions->mutex); if (p) iochan_activity(p->timeout_iochan); else error(rs, PAZPAR2_NO_SESSION, session); return p; }
void http_session_destroy(struct http_session *s) { int must_destroy = 0; http_sessions_t http_sessions = s->http_sessions; yaz_log(http_sessions->log_level, "Session %u destroy", s->session_id); yaz_mutex_enter(http_sessions->mutex); /* only if http_session has no active http sessions on it can be destroyed */ if (s->destroy_counter == s->activity_counter) { struct http_session **p = 0; must_destroy = 1; for (p = &http_sessions->session_list; *p; p = &(*p)->next) if (*p == s) { *p = (*p)->next; break; } } yaz_mutex_leave(http_sessions->mutex); if (must_destroy) { /* destroying for real */ yaz_log(http_sessions->log_level, "Session %u destroyed", s->session_id); iochan_destroy(s->timeout_iochan); session_destroy(s->psession); http_session_use(-1); nmem_destroy(s->nmem); } else { yaz_log(http_sessions->log_level, "Session %u destroying delayed. Active clients (%d-%d). Waiting for new timeout.", s->session_id, s->activity_counter, s->destroy_counter); } }
struct http_session *http_session_create(struct conf_service *service, http_sessions_t http_sessions, unsigned int sesid) { NMEM nmem = nmem_create(); struct http_session *r = nmem_malloc(nmem, sizeof(*r)); char tmp_str[50]; sprintf(tmp_str, "session#%u", sesid); r->psession = new_session(nmem, service, sesid); r->session_id = sesid; r->timestamp = 0; r->nmem = nmem; r->destroy_counter = r->activity_counter = 0; r->http_sessions = http_sessions; yaz_mutex_enter(http_sessions->mutex); r->next = http_sessions->session_list; http_sessions->session_list = r; yaz_mutex_leave(http_sessions->mutex); r->timeout_iochan = iochan_create(-1, session_timeout, 0, "http_session_timeout"); iochan_setdata(r->timeout_iochan, r); yaz_log(http_sessions->log_level, "Session %u created. timeout chan=%p timeout=%d", sesid, r->timeout_iochan, service->session_timeout); iochan_settimeout(r->timeout_iochan, service->session_timeout); iochan_add(service->server->iochan_man, r->timeout_iochan); http_session_use(1); return r; }
void *sel_thread_result(sel_thread_t p) { struct work_item *work_this = 0; void *data = 0; char read_buf[1]; yaz_mutex_enter(p->mutex); /* got something. Take the last one out of output_queue */ work_this = queue_remove_last(&p->output_queue); if (work_this) { /* put freed item in free list */ work_this->next = p->free_queue; p->free_queue = work_this; data = work_this->data; #ifdef WIN32 (void) recv(p->read_fd, read_buf, 1, 0); #else (void) read(p->read_fd, read_buf, 1); #endif } yaz_mutex_leave(p->mutex); return data; }
int pazpar2_decref(int *ref, YAZ_MUTEX mutex) { int value ; yaz_mutex_enter(mutex); value = --(*ref); yaz_mutex_leave(mutex); return value; }
static void *sel_thread_handler(void *vp) { sel_thread_t p = (sel_thread_t) vp; while (1) { struct work_item *work_this = 0; /* wait for some work */ yaz_mutex_enter(p->mutex); while (!p->stop_flag && !p->input_queue) yaz_cond_wait(p->input_data, p->mutex, 0); /* see if we were waken up because we're shutting down */ if (p->stop_flag) break; /* got something. Take the last one out of input_queue */ assert(p->input_queue); work_this = queue_remove_last(&p->input_queue); input_queue_length--; #if 0 yaz_log(YLOG_DEBUG, "input queue length after pop: %d", input_queue_length); #endif assert(work_this); yaz_mutex_leave(p->mutex); /* work on this item */ p->work_handler(work_this->data); /* put it back into output queue */ yaz_mutex_enter(p->mutex); work_this->next = p->output_queue; p->output_queue = work_this; yaz_mutex_leave(p->mutex); /* wake up select/poll with a single byte */ #ifdef WIN32 (void) send(p->write_fd, "", 1, 0); #else (void) write(p->write_fd, "", 1); #endif } yaz_mutex_leave(p->mutex); return 0; }
void iochan_add(iochan_man_t man, IOCHAN chan) { chan->man = man; yaz_mutex_enter(man->iochan_mutex); yaz_log(man->log_level, "iochan_add : chan=%p channel list=%p", chan, man->channel_list); chan->next = man->channel_list; man->channel_list = chan; yaz_mutex_leave(man->iochan_mutex); }
// Call after use of locate_session, in order to increment the destroy_counter static void release_session(struct http_channel *c, struct http_session *session) { http_sessions_t http_sessions = c->http_sessions; yaz_mutex_enter(http_sessions->mutex); if (session) session->destroy_counter++; yaz_mutex_leave(http_sessions->mutex); }
int iochans_count_total(void) { int total = 0; if (!g_mutex) return 0; yaz_mutex_enter(g_mutex); total = no_iochans_total; yaz_mutex_leave(g_mutex); return total; }
void connect_resolver_host(struct host *host, iochan_man_t iochan_man) { struct connection *con; start: yaz_mutex_enter(host->mutex); con = host->connections; while (con) { if (con->state == Conn_Closed) { if (!host->ipport) /* unresolved */ { remove_connection_from_host(con); yaz_mutex_leave(host->mutex); connection_destroy(con); goto start; /* start all over .. at some point it will be NULL */ } else if (!con->client) { remove_connection_from_host(con); yaz_mutex_leave(host->mutex); connection_destroy(con); /* start all over .. at some point it will be NULL */ goto start; } else { yaz_mutex_leave(host->mutex); connection_connect(con, iochan_man); client_start_search(con->client); goto start; } } else { yaz_log(YLOG_LOG, "connect_resolver_host: state=%d", con->state); con = con->next; } } yaz_mutex_leave(host->mutex); }
int http_session_use(int delta) { int sessions; if (!g_http_session_mutex) yaz_mutex_create(&g_http_session_mutex); yaz_mutex_enter(g_http_session_mutex); g_http_sessions += delta; sessions = g_http_sessions; yaz_mutex_leave(g_http_session_mutex); yaz_log(YLOG_DEBUG, "%s sessions=%d", delta == 0 ? "" : (delta > 0 ? "INC" : "DEC"), sessions); return sessions; }
static int iochan_use(int delta) { int iochans; if (!g_mutex) yaz_mutex_create(&g_mutex); yaz_mutex_enter(g_mutex); no_iochans += delta; if (delta > 0) no_iochans_total += delta; iochans = no_iochans; yaz_mutex_leave(g_mutex); yaz_log(YLOG_DEBUG, "%s iochans=%d", delta == 0 ? "" : (delta > 0 ? "INC" : "DEC"), iochans); return iochans; }
static int connection_use(int delta) { int result; if (!g_mutex) yaz_mutex_create(&g_mutex); yaz_mutex_enter(g_mutex); no_connections += delta; result = no_connections; if (delta > 0) total_no_connections += delta; yaz_mutex_leave(g_mutex); if (delta == 0) return result; yaz_log(YLOG_LOG, "%s connections=%d", delta > 0 ? "INC" : "DEC", no_connections); return result; }
void iochan_man_destroy(iochan_man_t *mp) { if (*mp) { IOCHAN c; if ((*mp)->sel_thread) sel_thread_destroy((*mp)->sel_thread); yaz_mutex_enter((*mp)->iochan_mutex); c = (*mp)->channel_list; (*mp)->channel_list = NULL; yaz_mutex_leave((*mp)->iochan_mutex); while (c) { c = iochan_destroy_real(c); } yaz_mutex_destroy(&(*mp)->iochan_mutex); xfree(*mp); *mp = 0; } }
void http_server_destroy(http_server_t hs) { if (hs) { int r; yaz_mutex_enter(hs->mutex); /* OK: hs->mutex may be NULL */ r = --(hs->ref_count); yaz_mutex_leave(hs->mutex); if (r == 0) { http_sessions_destroy(hs->http_sessions); xfree(hs->proxy_addr); yaz_mutex_destroy(&hs->mutex); if (hs->record_file) fclose(hs->record_file); xfree(hs); } } }
void sel_thread_destroy(sel_thread_t p) { int i; yaz_mutex_enter(p->mutex); p->stop_flag = 1; yaz_cond_broadcast(p->input_data); yaz_mutex_leave(p->mutex); for (i = 0; i< p->no_threads; i++) yaz_thread_join(&p->thread_id[i], 0); if (p->work_destroy) { queue_trav(p->input_queue, p->work_destroy); queue_trav(p->output_queue, p->work_destroy); } yaz_spipe_destroy(p->spipe); yaz_cond_destroy(&p->input_data); yaz_mutex_destroy(&p->mutex); nmem_destroy(p->nmem); }
void sel_thread_add(sel_thread_t p, void *data) { struct work_item *work_p; yaz_mutex_enter(p->mutex); if (p->free_queue) { work_p = p->free_queue; p->free_queue = p->free_queue->next; } else work_p = nmem_malloc(p->nmem, sizeof(*work_p)); work_p->data = data; work_p->next = p->input_queue; p->input_queue = work_p; input_queue_length++; #if 0 yaz_log(YLOG_DEBUG, "sel_thread_add: Input queue length after push: %d", input_queue_length); #endif yaz_cond_signal(p->input_data); yaz_mutex_leave(p->mutex); }
void pazpar2_incref(int *ref, YAZ_MUTEX mutex) { yaz_mutex_enter(mutex); (*ref)++; yaz_mutex_leave(mutex); }
// Ensure that client has a connection associated int client_prep_connection(struct client *cl, int operation_timeout, int session_timeout, iochan_man_t iochan_man, const struct timeval *abstime) { struct connection *co; struct session_database *sdb = client_get_database(cl); const char *zproxy = session_setting_oneval(sdb, PZ_ZPROXY); const char *url = session_setting_oneval(sdb, PZ_URL); const char *sru = session_setting_oneval(sdb, PZ_SRU); struct host *host = 0; int default_port = *sru ? 80 : 210; if (zproxy && zproxy[0] == '\0') zproxy = 0; if (!url || !*url) url = sdb->database->id; host = find_host(client_get_session(cl)->service->server->database_hosts, url, zproxy, default_port, iochan_man); yaz_log(YLOG_DEBUG, "client_prep_connection: target=%s url=%s", client_get_id(cl), url); if (!host) return 0; co = client_get_connection(cl); if (co) { assert(co->host); if (co->host == host && client_get_state(cl) == Client_Idle) { return 2; } client_incref(cl); connection_release(co); co = 0; } if (!co) { int max_connections = 0; int reuse_connections = 1; const char *v = session_setting_oneval(client_get_database(cl), PZ_MAX_CONNECTIONS); if (v && *v) max_connections = atoi(v); v = session_setting_oneval(client_get_database(cl), PZ_REUSE_CONNECTIONS); if (v && *v) reuse_connections = atoi(v); // See if someone else has an idle connection // We should look at timestamps here to select the longest-idle connection yaz_mutex_enter(host->mutex); while (1) { int num_connections = 0; for (co = host->connections; co; co = co->next) num_connections++; if (reuse_connections) { for (co = host->connections; co; co = co->next) { if (connection_is_idle(co) && (!co->client || client_get_state(co->client) == Client_Idle) && !strcmp(ZOOM_connection_option_get(co->link, "user"), session_setting_oneval(client_get_database(cl), PZ_AUTHENTICATION))) { if (zproxy == 0 && co->zproxy == 0) break; if (zproxy && co->zproxy && !strcmp(zproxy, co->zproxy)) break; } } if (co) { yaz_log(YLOG_LOG, "num_connections = %d (reusing)", num_connections); break; } } if (max_connections <= 0 || num_connections < max_connections) { yaz_log(YLOG_LOG, "num_connections = %d (new); max = %d", num_connections, max_connections); break; } yaz_log(YLOG_LOG, "num_connections = %d (waiting) max = %d", num_connections, max_connections); if (yaz_cond_wait(host->cond_ready, host->mutex, abstime)) { yaz_log(YLOG_LOG, "out of connections %s", client_get_id(cl)); client_set_state(cl, Client_Error); yaz_mutex_leave(host->mutex); return 0; } } if (co) { yaz_log(YLOG_LOG, "%p Connection reuse. state: %d", co, co->state); connection_release(co); client_set_connection(cl, co); co->client = cl; /* ensure that connection is only assigned to this client by marking the client non Idle */ client_set_state(cl, Client_Working); yaz_mutex_leave(host->mutex); co->operation_timeout = operation_timeout; co->session_timeout = session_timeout; /* tells ZOOM to reconnect if necessary. Disabled becuase the ZOOM_connection_connect flushes the task queue */ ZOOM_connection_connect(co->link, 0, 0); } else { yaz_mutex_leave(host->mutex); co = connection_create(cl, host, operation_timeout, session_timeout, iochan_man); } assert(co->host); } if (co && co->link) return 1; else return 0; }
static int event_loop(iochan_man_t man, IOCHAN *iochans) { do /* loop as long as there are active associations to process */ { IOCHAN p, *nextp; IOCHAN start; IOCHAN inv_start; fd_set in, out, except; int res, max; static struct timeval to; struct timeval *timeout; // struct yaz_poll_fd *fds; int no_fds = 0; FD_ZERO(&in); FD_ZERO(&out); FD_ZERO(&except); timeout = &to; /* hang on select */ to.tv_sec = 300; to.tv_usec = 0; // INV: start must no change through the loop yaz_mutex_enter(man->iochan_mutex); start = man->channel_list; yaz_mutex_leave(man->iochan_mutex); inv_start = start; for (p = start; p; p = p->next) { no_fds++; } // fds = (struct yaz_poll_fd *) xmalloc(no_fds * sizeof(*fds)); max = 0; for (p = start; p; p = p->next) { if (p->thread_users > 0) continue; if (p->max_idle && p->max_idle < to.tv_sec) to.tv_sec = p->max_idle; if (p->fd < 0) continue; if (p->flags & EVENT_INPUT) FD_SET(p->fd, &in); if (p->flags & EVENT_OUTPUT) FD_SET(p->fd, &out); if (p->flags & EVENT_EXCEPT) FD_SET(p->fd, &except); if (p->fd > max) max = p->fd; } yaz_log(man->log_level, "max=%d sel_fd=%d", max, man->sel_fd); if (man->sel_fd != -1) { if (man->sel_fd > max) max = man->sel_fd; FD_SET(man->sel_fd, &in); } yaz_log(man->log_level, "select begin nofds=%d", max); res = select(max + 1, &in, &out, &except, timeout); yaz_log(man->log_level, "select returned res=%d", res); if (res < 0) { if (errno == EINTR) continue; else { yaz_log(YLOG_ERRNO | YLOG_WARN, "select"); return 0; } } if (man->sel_fd != -1) { if (FD_ISSET(man->sel_fd, &in)) { IOCHAN chan; yaz_log(man->log_level, "eventl: sel input on sel_fd=%d", man->sel_fd); while ((chan = sel_thread_result(man->sel_thread))) { yaz_log(man->log_level, "eventl: got thread result chan=%p name=%s", chan, chan->name ? chan->name : ""); chan->thread_users--; } } } if (man->log_level) { int no = 0; for (p = start; p; p = p->next) { no++; } yaz_log(man->log_level, "%d channels", no); } for (p = start; p; p = p->next) { time_t now = time(0); if (p->destroyed) { yaz_log(man->log_level, "eventl: skip destroyed chan=%p name=%s", p, p->name ? p->name : ""); continue; } if (p->thread_users > 0) { yaz_log(man->log_level, "eventl: skip chan=%p name=%s users=%d", p, p->name ? p->name : "", p->thread_users); continue; } p->this_event = 0; if (p->max_idle && now - p->last_event > p->max_idle) { p->last_event = now; p->this_event |= EVENT_TIMEOUT; } if (p->fd >= 0) { if (FD_ISSET(p->fd, &in)) { p->last_event = now; p->this_event |= EVENT_INPUT; } if (FD_ISSET(p->fd, &out)) { p->last_event = now; p->this_event |= EVENT_OUTPUT; } if (FD_ISSET(p->fd, &except)) { p->last_event = now; p->this_event |= EVENT_EXCEPT; } } run_fun(man, p); } assert(inv_start == start); yaz_mutex_enter(man->iochan_mutex); for (nextp = iochans; *nextp;) { IOCHAN p = *nextp; if (p->destroyed && p->thread_users == 0) { *nextp = iochan_destroy_real(p); } else nextp = &p->next; } yaz_mutex_leave(man->iochan_mutex); } while (*iochans); return 0; }