commit be4b1c7e7be915bb6cd68e24b7e2574d897a5500
parent 5090ebea20215fe14b5d5250eba6b7b9ddd13b8a
Author: Joris Vink <joris@coders.se>
Date: Wed, 2 Jul 2014 12:19:38 +0200
Move actual code out of contrib into src/.
Diffstat:
Makefile | | | 4 | ++-- |
contrib/postgres/kore_pgsql.c | | | 346 | ------------------------------------------------------------------------------- |
contrib/tasks/kore_tasks.c | | | 291 | ------------------------------------------------------------------------------ |
src/pgsql.c | | | 346 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
src/tasks.c | | | 291 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
5 files changed, 639 insertions(+), 639 deletions(-)
diff --git a/Makefile b/Makefile
@@ -23,13 +23,13 @@ ifneq ("$(KORE_PEDANTIC_MALLOC)", "")
endif
ifneq ("$(PGSQL)", "")
- S_SRC+=contrib/postgres/kore_pgsql.c
+ S_SRC+=src/pgsql.c
LDFLAGS+=-L$(shell pg_config --libdir) -lpq
CFLAGS+=-I$(shell pg_config --includedir) -DKORE_USE_PGSQL
endif
ifneq ("$(TASKS)", "")
- S_SRC+=contrib/tasks/kore_tasks.c
+ S_SRC+=src/tasks.c
LDFLAGS+=-lpthread
CFLAGS+=-DKORE_USE_TASKS
endif
diff --git a/contrib/postgres/kore_pgsql.c b/contrib/postgres/kore_pgsql.c
@@ -1,346 +0,0 @@
-/*
- * Copyright (c) 2014 Joris Vink <joris@coders.se>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sys/param.h>
-#include <sys/queue.h>
-
-#include <libpq-fe.h>
-
-#include "kore.h"
-#include "http.h"
-#include "kore_pgsql.h"
-
-struct pgsql_job {
- u_int8_t idx;
- struct http_request *req;
- u_int64_t start;
- char *query;
-
- TAILQ_ENTRY(pgsql_job) list;
-};
-
-#define PGSQL_CONN_MAX 2
-#define PGSQL_CONN_FREE 0x01
-
-struct pgsql_conn {
- u_int8_t type;
- u_int8_t flags;
-
- PGconn *db;
- struct pgsql_job *job;
- TAILQ_ENTRY(pgsql_conn) list;
-};
-
-static void pgsql_conn_cleanup(struct pgsql_conn *);
-static int pgsql_conn_create(struct http_request *, int);
-static void pgsql_read_result(struct http_request *, int,
- struct pgsql_conn *);
-
-static TAILQ_HEAD(, pgsql_conn) pgsql_conn_free;
-static u_int16_t pgsql_conn_count;
-char *pgsql_conn_string = NULL;
-
-void
-kore_pgsql_init(void)
-{
- pgsql_conn_count = 0;
- TAILQ_INIT(&pgsql_conn_free);
-}
-
-int
-kore_pgsql_query(struct http_request *req, char *query, int idx)
-{
- int fd;
- struct pgsql_conn *conn;
-
- if (idx >= HTTP_PGSQL_MAX)
- fatal("kore_pgsql_query: %d > %d", idx, HTTP_PGSQL_MAX);
- if (req->pgsql[idx] != NULL)
- fatal("kore_pgsql_query: %d already exists", idx);
-
- if (TAILQ_EMPTY(&pgsql_conn_free)) {
- if (pgsql_conn_count >= PGSQL_CONN_MAX)
- return (KORE_RESULT_ERROR);
- }
-
- req->pgsql[idx] = kore_malloc(sizeof(struct kore_pgsql));
- req->pgsql[idx]->state = KORE_PGSQL_STATE_INIT;
- req->pgsql[idx]->result = NULL;
- req->pgsql[idx]->error = NULL;
- req->pgsql[idx]->conn = NULL;
-
- if (TAILQ_EMPTY(&pgsql_conn_free)) {
- if (pgsql_conn_create(req, idx) == KORE_RESULT_ERROR)
- return (KORE_RESULT_ERROR);
- }
-
- req->flags |= HTTP_REQUEST_SLEEPING;
- conn = TAILQ_FIRST(&pgsql_conn_free);
- if (!(conn->flags & PGSQL_CONN_FREE))
- fatal("received a pgsql conn that was not free?");
-
- conn->flags &= ~PGSQL_CONN_FREE;
- TAILQ_REMOVE(&pgsql_conn_free, conn, list);
-
- req->pgsql[idx]->conn = conn;
- conn->job = kore_malloc(sizeof(struct pgsql_job));
- conn->job->query = kore_strdup(query);
- conn->job->start = kore_time_ms();
- conn->job->req = req;
- conn->job->idx = idx;
-
- if (!PQsendQuery(conn->db, query)) {
- pgsql_conn_cleanup(conn);
- return (KORE_RESULT_ERROR);
- }
-
- fd = PQsocket(conn->db);
- if (fd < 0)
- fatal("PQsocket returned < 0 fd on open connection");
-
- kore_platform_schedule_read(fd, conn);
- req->pgsql[idx]->state = KORE_PGSQL_STATE_WAIT;
- kore_debug("query '%s' for %p sent on %p", query, req, conn);
-
- return (KORE_RESULT_OK);
-}
-
-void
-kore_pgsql_handle(void *c, int err)
-{
- int i;
- struct http_request *req;
- struct pgsql_conn *conn = (struct pgsql_conn *)c;
-
- if (err) {
- pgsql_conn_cleanup(conn);
- return;
- }
-
- i = conn->job->idx;
- req = conn->job->req;
- kore_debug("kore_pgsql_handle: %p (%d) (%d)",
- req, i, req->pgsql[i]->state);
-
- if (!PQconsumeInput(conn->db)) {
- req->pgsql[i]->state = KORE_PGSQL_STATE_ERROR;
- req->pgsql[i]->error = kore_strdup(PQerrorMessage(conn->db));
- } else {
- pgsql_read_result(req, i, conn);
- }
-
- if (req->pgsql[i]->state == KORE_PGSQL_STATE_WAIT) {
- req->flags |= HTTP_REQUEST_SLEEPING;
- } else {
- req->flags &= ~HTTP_REQUEST_SLEEPING;
- http_process_request(req, 1);
- }
-}
-
-void
-kore_pgsql_continue(struct http_request *req, int i)
-{
- int fd;
- struct pgsql_conn *conn;
-
- kore_debug("kore_pgsql_continue: %p->%p (%d) (%d)",
- req->owner, req, i, req->pgsql[i]->state);
-
- if (req->pgsql[i]->error) {
- kore_mem_free(req->pgsql[i]->error);
- req->pgsql[i]->error = NULL;
- }
-
- if (req->pgsql[i]->result)
- PQclear(req->pgsql[i]->result);
-
- conn = req->pgsql[i]->conn;
- switch (req->pgsql[i]->state) {
- case KORE_PGSQL_STATE_INIT:
- case KORE_PGSQL_STATE_WAIT:
- break;
- case KORE_PGSQL_STATE_DONE:
- req->pgsql[i]->conn = NULL;
- req->flags &= ~HTTP_REQUEST_SLEEPING;
- req->pgsql[i]->state = KORE_PGSQL_STATE_COMPLETE;
-
- kore_mem_free(conn->job->query);
- kore_mem_free(conn->job);
-
- conn->job = NULL;
- conn->flags |= PGSQL_CONN_FREE;
- TAILQ_INSERT_TAIL(&pgsql_conn_free, conn, list);
-
- fd = PQsocket(conn->db);
- kore_platform_disable_read(fd);
-
- http_process_request(req, 0);
- break;
- case KORE_PGSQL_STATE_ERROR:
- case KORE_PGSQL_STATE_RESULT:
- kore_pgsql_handle(conn, 0);
- break;
- default:
- fatal("unknown pgsql state");
- }
-}
-
-void
-kore_pgsql_cleanup(struct http_request *req)
-{
- int i;
- struct pgsql_conn *conn;
-
- for (i = 0; i < HTTP_PGSQL_MAX; i++) {
- if (req->pgsql[i] == NULL)
- continue;
-
- if (req->pgsql[i]->result != NULL) {
- kore_log(LOG_NOTICE, "cleaning up leaked pgsql result");
- PQclear(req->pgsql[i]->result);
- }
-
- if (req->pgsql[i]->error != NULL)
- kore_mem_free(req->pgsql[i]->error);
-
- if (req->pgsql[i]->conn != NULL) {
- conn = req->pgsql[i]->conn;
- while (PQgetResult(conn->db) != NULL)
- ;
- }
-
- req->pgsql[i]->conn = NULL;
- kore_mem_free(req->pgsql[i]);
- req->pgsql[i] = NULL;
- }
-}
-
-void
-kore_pgsql_logerror(struct kore_pgsql *pgsql)
-{
- kore_log(LOG_NOTICE, "pgsql error: %s",
- (pgsql->error) ? pgsql->error : "unknown");
-}
-
-int
-kore_pgsql_ntuples(struct kore_pgsql *pgsql)
-{
- return (PQntuples(pgsql->result));
-}
-
-char *
-kore_pgsql_getvalue(struct kore_pgsql *pgsql, int row, int col)
-{
- return (PQgetvalue(pgsql->result, row, col));
-}
-
-static int
-pgsql_conn_create(struct http_request *req, int idx)
-{
- struct pgsql_conn *conn;
-
- if (pgsql_conn_string == NULL)
- fatal("pgsql_conn_create: no connection string");
-
- pgsql_conn_count++;
- conn = kore_malloc(sizeof(*conn));
- kore_debug("pgsql_conn_create(): %p", conn);
- memset(conn, 0, sizeof(*conn));
-
- conn->db = PQconnectdb(pgsql_conn_string);
- if (conn->db == NULL || (PQstatus(conn->db) != CONNECTION_OK)) {
- req->pgsql[idx]->state = KORE_PGSQL_STATE_ERROR;
- req->pgsql[idx]->error = kore_strdup(PQerrorMessage(conn->db));
- pgsql_conn_cleanup(conn);
- return (KORE_RESULT_ERROR);
- }
-
- conn->job = NULL;
- conn->flags = PGSQL_CONN_FREE;
- conn->type = KORE_TYPE_PGSQL_CONN;
- TAILQ_INSERT_TAIL(&pgsql_conn_free, conn, list);
-
- return (KORE_RESULT_OK);
-}
-
-static void
-pgsql_conn_cleanup(struct pgsql_conn *conn)
-{
- int i;
- struct http_request *req;
-
- kore_debug("pgsql_conn_cleanup(): %p", conn);
-
- if (conn->flags & PGSQL_CONN_FREE)
- TAILQ_REMOVE(&pgsql_conn_free, conn, list);
-
- if (conn->job) {
- i = conn->job->idx;
- req = conn->job->req;
-
- req->pgsql[i]->conn = NULL;
- req->pgsql[i]->state = KORE_PGSQL_STATE_ERROR;
- req->pgsql[i]->error = kore_strdup(PQerrorMessage(conn->db));
- req->flags &= ~HTTP_REQUEST_SLEEPING;
-
- kore_mem_free(conn->job->query);
- kore_mem_free(conn->job);
- conn->job = NULL;
- }
-
- if (conn->db != NULL)
- PQfinish(conn->db);
-
- pgsql_conn_count--;
- kore_mem_free(conn);
-}
-
-static void
-pgsql_read_result(struct http_request *req, int i, struct pgsql_conn *conn)
-{
- if (PQisBusy(conn->db)) {
- req->pgsql[i]->state = KORE_PGSQL_STATE_WAIT;
- return;
- }
-
- req->pgsql[i]->result = PQgetResult(conn->db);
- if (req->pgsql[i]->result == NULL) {
- req->pgsql[i]->state = KORE_PGSQL_STATE_DONE;
- return;
- }
-
- switch (PQresultStatus(req->pgsql[i]->result)) {
- case PGRES_COPY_OUT:
- case PGRES_COPY_IN:
- case PGRES_NONFATAL_ERROR:
- case PGRES_COPY_BOTH:
- break;
- case PGRES_COMMAND_OK:
- req->pgsql[i]->state = KORE_PGSQL_STATE_DONE;
- break;
- case PGRES_TUPLES_OK:
- case PGRES_SINGLE_TUPLE:
- req->pgsql[i]->state = KORE_PGSQL_STATE_RESULT;
- break;
- case PGRES_EMPTY_QUERY:
- case PGRES_BAD_RESPONSE:
- case PGRES_FATAL_ERROR:
- req->pgsql[i]->state = KORE_PGSQL_STATE_ERROR;
- req->pgsql[i]->error =
- kore_strdup(PQresultErrorMessage(req->pgsql[i]->result));
- break;
- }
-}
diff --git a/contrib/tasks/kore_tasks.c b/contrib/tasks/kore_tasks.c
@@ -1,291 +0,0 @@
-/*
- * Copyright (c) 2014 Joris Vink <joris@coders.se>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sys/param.h>
-#include <sys/queue.h>
-#include <sys/socket.h>
-
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "kore.h"
-#include "http.h"
-#include "kore_tasks.h"
-
-static u_int8_t threads;
-static pthread_mutex_t task_thread_lock;
-
-static TAILQ_HEAD(, kore_task_thread) task_threads;
-
-static void *task_thread(void *);
-static void task_channel_read(int, void *, u_int32_t);
-static void task_channel_write(int, void *, u_int32_t);
-static void task_thread_spawn(struct kore_task_thread **);
-
-#define THREAD_FD_ASSIGN(t, f, i, o) \
- do { \
- if (pthread_self() == t) { \
- f = i; \
- } else { \
- f = o; \
- } \
- } while (0);
-
-void
-kore_task_init(void)
-{
- threads = 0;
-
- TAILQ_INIT(&task_threads);
- pthread_mutex_init(&task_thread_lock, NULL);
-}
-
-void
-kore_task_create(struct kore_task **out, int (*entry)(struct kore_task *))
-{
- struct kore_task *t;
-
- t = kore_malloc(sizeof(struct kore_task));
-
- t->req = NULL;
- t->entry = entry;
- t->type = KORE_TYPE_TASK;
- t->state = KORE_TASK_STATE_CREATED;
-
- if (socketpair(AF_UNIX, SOCK_STREAM, 0,t->fds) == -1)
- fatal("kore_task_create: socketpair() %s", errno_s);
-
- if (out != NULL)
- *out = t;
-}
-
-void
-kore_task_run(struct kore_task *t)
-{
- struct kore_task_thread *tt;
-
- pthread_mutex_lock(&task_thread_lock);
- if (TAILQ_EMPTY(&task_threads))
- task_thread_spawn(&tt);
- else
- tt = TAILQ_FIRST(&task_threads);
-
- pthread_mutex_unlock(&task_thread_lock);
- pthread_mutex_lock(&(tt->lock));
-
- t->thread = tt;
- TAILQ_INSERT_TAIL(&(tt->tasks), t, list);
-
- pthread_mutex_unlock(&(tt->lock));
- pthread_cond_signal(&(tt->cond));
-}
-
-void
-kore_task_bind_request(struct kore_task *t, struct http_request *req)
-{
- kore_debug("kore_task_bind_request: %p bound to %p", req, t);
-
- t->req = req;
- req->task = t;
- req->flags |= HTTP_REQUEST_SLEEPING;
-
- kore_platform_schedule_read(t->fds[0], t);
-}
-
-void
-kore_task_destroy(struct kore_task *t)
-{
- kore_debug("kore_task_destroy: %p", t);
-
- if (t->req != NULL)
- t->req->task = NULL;
-
- close(t->fds[0]);
- close(t->fds[1]); /* This might already be closed. */
-
- kore_mem_free(t);
-}
-
-int
-kore_task_finished(struct kore_task *t)
-{
- if (t->state == KORE_TASK_STATE_FINISHED)
- return (1);
-
- return (0);
-}
-
-void
-kore_task_finish(struct kore_task *t)
-{
- kore_debug("kore_task_finished: %p (%d)", t, t->result);
- close(t->fds[1]);
-}
-
-void
-kore_task_channel_write(struct kore_task *t, void *data, u_int32_t len)
-{
- int fd;
-
- kore_debug("kore_task_channel_write: %p <- %p (%ld)", t, data, len);
-
- THREAD_FD_ASSIGN(t->thread->tid, fd, t->fds[1], t->fds[0]);
- task_channel_write(fd, &len, sizeof(len));
- task_channel_write(fd, data, len);
-}
-
-u_int32_t
-kore_task_channel_read(struct kore_task *t, void *out, u_int32_t len)
-{
- int fd;
- u_int32_t dlen, bytes;
-
- kore_debug("kore_task_channel_read: %p -> %p (%ld)", t, out, len);
-
- THREAD_FD_ASSIGN(t->thread->tid, fd, t->fds[1], t->fds[0]);
- task_channel_read(fd, &dlen, sizeof(dlen));
-
- if (dlen > len)
- bytes = len;
- else
- bytes = dlen;
-
- task_channel_read(fd, out, bytes);
-
- return (dlen);
-}
-
-void
-kore_task_handle(struct kore_task *t, int finished)
-{
- kore_debug("kore_task_handle: %p, %d", t, finished);
-
- if (finished) {
- t->state = KORE_TASK_STATE_FINISHED;
- if (t->req != NULL) {
- t->req->flags &= ~HTTP_REQUEST_SLEEPING;
- if (t->req->flags & HTTP_REQUEST_DELETE)
- kore_task_destroy(t);
- }
- }
-}
-
-static void
-task_channel_write(int fd, void *data, u_int32_t len)
-{
- ssize_t r;
- u_int8_t *d;
- u_int32_t offset;
-
- d = data;
- offset = 0;
- while (offset != len) {
- r = write(fd, d + offset, len - offset);
- if (r == -1 && errno == EINTR)
- continue;
- if (r == -1)
- fatal("task_channel_write: %s", errno_s);
- offset += r;
- }
-}
-
-static void
-task_channel_read(int fd, void *out, u_int32_t len)
-{
- ssize_t r;
- u_int8_t *d;
- u_int32_t offset;
-
- d = out;
- offset = 0;
- while (offset != len) {
- r = read(fd, d + offset, len - offset);
- if (r == -1 && errno == EINTR)
- continue;
- if (r == -1)
- fatal("task_channel_read: %s", errno_s);
- if (r == 0)
- fatal("task_channel_read: unexpected eof");
-
- offset += r;
- }
-}
-
-static void
-task_thread_spawn(struct kore_task_thread **out)
-{
- struct kore_task_thread *tt;
-
- tt = kore_malloc(sizeof(*tt));
- tt->idx = threads++;
-
- TAILQ_INIT(&(tt->tasks));
- pthread_cond_init(&(tt->cond), NULL);
- pthread_mutex_init(&(tt->lock), NULL);
-
- if (pthread_create(&(tt->tid), NULL, task_thread, tt) != 0)
- fatal("pthread_create: %s", errno_s);
-
- *out = tt;
-}
-
-static void *
-task_thread(void *arg)
-{
- struct kore_task *t;
- struct kore_task_thread *tt = arg;
-
- kore_debug("task_thread: #%d starting", tt->idx);
-
- pthread_mutex_lock(&(tt->lock));
-
- pthread_mutex_lock(&task_thread_lock);
- TAILQ_INSERT_TAIL(&task_threads, tt, list);
- pthread_mutex_unlock(&task_thread_lock);
-
- for (;;) {
- if (TAILQ_EMPTY(&(tt->tasks)))
- pthread_cond_wait(&(tt->cond), &(tt->lock));
-
- kore_debug("task_thread#%d: woke up", tt->idx);
-
- t = TAILQ_FIRST(&(tt->tasks));
- TAILQ_REMOVE(&(tt->tasks), t, list);
- pthread_mutex_unlock(&(tt->lock));
-
- pthread_mutex_lock(&task_thread_lock);
- TAILQ_REMOVE(&task_threads, tt, list);
- pthread_mutex_unlock(&task_thread_lock);
-
- kore_debug("task_thread#%d: executing %p", tt->idx, t);
-
- t->state = KORE_TASK_STATE_RUNNING;
- t->result = t->entry(t);
- kore_task_finish(t);
-
- pthread_mutex_lock(&task_thread_lock);
- TAILQ_INSERT_HEAD(&task_threads, tt, list);
- pthread_mutex_unlock(&task_thread_lock);
-
- pthread_mutex_lock(&(tt->lock));
- }
-
- pthread_exit(NULL);
-
- /* NOTREACHED */
- return (NULL);
-}
diff --git a/src/pgsql.c b/src/pgsql.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 2014 Joris Vink <joris@coders.se>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+
+#include <libpq-fe.h>
+
+#include "kore.h"
+#include "http.h"
+#include "kore_pgsql.h"
+
+struct pgsql_job {
+ u_int8_t idx;
+ struct http_request *req;
+ u_int64_t start;
+ char *query;
+
+ TAILQ_ENTRY(pgsql_job) list;
+};
+
+#define PGSQL_CONN_MAX 2
+#define PGSQL_CONN_FREE 0x01
+
+struct pgsql_conn {
+ u_int8_t type;
+ u_int8_t flags;
+
+ PGconn *db;
+ struct pgsql_job *job;
+ TAILQ_ENTRY(pgsql_conn) list;
+};
+
+static void pgsql_conn_cleanup(struct pgsql_conn *);
+static int pgsql_conn_create(struct http_request *, int);
+static void pgsql_read_result(struct http_request *, int,
+ struct pgsql_conn *);
+
+static TAILQ_HEAD(, pgsql_conn) pgsql_conn_free;
+static u_int16_t pgsql_conn_count;
+char *pgsql_conn_string = NULL;
+
+void
+kore_pgsql_init(void)
+{
+ pgsql_conn_count = 0;
+ TAILQ_INIT(&pgsql_conn_free);
+}
+
+int
+kore_pgsql_query(struct http_request *req, char *query, int idx)
+{
+ int fd;
+ struct pgsql_conn *conn;
+
+ if (idx >= HTTP_PGSQL_MAX)
+ fatal("kore_pgsql_query: %d > %d", idx, HTTP_PGSQL_MAX);
+ if (req->pgsql[idx] != NULL)
+ fatal("kore_pgsql_query: %d already exists", idx);
+
+ if (TAILQ_EMPTY(&pgsql_conn_free)) {
+ if (pgsql_conn_count >= PGSQL_CONN_MAX)
+ return (KORE_RESULT_ERROR);
+ }
+
+ req->pgsql[idx] = kore_malloc(sizeof(struct kore_pgsql));
+ req->pgsql[idx]->state = KORE_PGSQL_STATE_INIT;
+ req->pgsql[idx]->result = NULL;
+ req->pgsql[idx]->error = NULL;
+ req->pgsql[idx]->conn = NULL;
+
+ if (TAILQ_EMPTY(&pgsql_conn_free)) {
+ if (pgsql_conn_create(req, idx) == KORE_RESULT_ERROR)
+ return (KORE_RESULT_ERROR);
+ }
+
+ req->flags |= HTTP_REQUEST_SLEEPING;
+ conn = TAILQ_FIRST(&pgsql_conn_free);
+ if (!(conn->flags & PGSQL_CONN_FREE))
+ fatal("received a pgsql conn that was not free?");
+
+ conn->flags &= ~PGSQL_CONN_FREE;
+ TAILQ_REMOVE(&pgsql_conn_free, conn, list);
+
+ req->pgsql[idx]->conn = conn;
+ conn->job = kore_malloc(sizeof(struct pgsql_job));
+ conn->job->query = kore_strdup(query);
+ conn->job->start = kore_time_ms();
+ conn->job->req = req;
+ conn->job->idx = idx;
+
+ if (!PQsendQuery(conn->db, query)) {
+ pgsql_conn_cleanup(conn);
+ return (KORE_RESULT_ERROR);
+ }
+
+ fd = PQsocket(conn->db);
+ if (fd < 0)
+ fatal("PQsocket returned < 0 fd on open connection");
+
+ kore_platform_schedule_read(fd, conn);
+ req->pgsql[idx]->state = KORE_PGSQL_STATE_WAIT;
+ kore_debug("query '%s' for %p sent on %p", query, req, conn);
+
+ return (KORE_RESULT_OK);
+}
+
+void
+kore_pgsql_handle(void *c, int err)
+{
+ int i;
+ struct http_request *req;
+ struct pgsql_conn *conn = (struct pgsql_conn *)c;
+
+ if (err) {
+ pgsql_conn_cleanup(conn);
+ return;
+ }
+
+ i = conn->job->idx;
+ req = conn->job->req;
+ kore_debug("kore_pgsql_handle: %p (%d) (%d)",
+ req, i, req->pgsql[i]->state);
+
+ if (!PQconsumeInput(conn->db)) {
+ req->pgsql[i]->state = KORE_PGSQL_STATE_ERROR;
+ req->pgsql[i]->error = kore_strdup(PQerrorMessage(conn->db));
+ } else {
+ pgsql_read_result(req, i, conn);
+ }
+
+ if (req->pgsql[i]->state == KORE_PGSQL_STATE_WAIT) {
+ req->flags |= HTTP_REQUEST_SLEEPING;
+ } else {
+ req->flags &= ~HTTP_REQUEST_SLEEPING;
+ http_process_request(req, 1);
+ }
+}
+
+void
+kore_pgsql_continue(struct http_request *req, int i)
+{
+ int fd;
+ struct pgsql_conn *conn;
+
+ kore_debug("kore_pgsql_continue: %p->%p (%d) (%d)",
+ req->owner, req, i, req->pgsql[i]->state);
+
+ if (req->pgsql[i]->error) {
+ kore_mem_free(req->pgsql[i]->error);
+ req->pgsql[i]->error = NULL;
+ }
+
+ if (req->pgsql[i]->result)
+ PQclear(req->pgsql[i]->result);
+
+ conn = req->pgsql[i]->conn;
+ switch (req->pgsql[i]->state) {
+ case KORE_PGSQL_STATE_INIT:
+ case KORE_PGSQL_STATE_WAIT:
+ break;
+ case KORE_PGSQL_STATE_DONE:
+ req->pgsql[i]->conn = NULL;
+ req->flags &= ~HTTP_REQUEST_SLEEPING;
+ req->pgsql[i]->state = KORE_PGSQL_STATE_COMPLETE;
+
+ kore_mem_free(conn->job->query);
+ kore_mem_free(conn->job);
+
+ conn->job = NULL;
+ conn->flags |= PGSQL_CONN_FREE;
+ TAILQ_INSERT_TAIL(&pgsql_conn_free, conn, list);
+
+ fd = PQsocket(conn->db);
+ kore_platform_disable_read(fd);
+
+ http_process_request(req, 0);
+ break;
+ case KORE_PGSQL_STATE_ERROR:
+ case KORE_PGSQL_STATE_RESULT:
+ kore_pgsql_handle(conn, 0);
+ break;
+ default:
+ fatal("unknown pgsql state");
+ }
+}
+
+void
+kore_pgsql_cleanup(struct http_request *req)
+{
+ int i;
+ struct pgsql_conn *conn;
+
+ for (i = 0; i < HTTP_PGSQL_MAX; i++) {
+ if (req->pgsql[i] == NULL)
+ continue;
+
+ if (req->pgsql[i]->result != NULL) {
+ kore_log(LOG_NOTICE, "cleaning up leaked pgsql result");
+ PQclear(req->pgsql[i]->result);
+ }
+
+ if (req->pgsql[i]->error != NULL)
+ kore_mem_free(req->pgsql[i]->error);
+
+ if (req->pgsql[i]->conn != NULL) {
+ conn = req->pgsql[i]->conn;
+ while (PQgetResult(conn->db) != NULL)
+ ;
+ }
+
+ req->pgsql[i]->conn = NULL;
+ kore_mem_free(req->pgsql[i]);
+ req->pgsql[i] = NULL;
+ }
+}
+
+void
+kore_pgsql_logerror(struct kore_pgsql *pgsql)
+{
+ kore_log(LOG_NOTICE, "pgsql error: %s",
+ (pgsql->error) ? pgsql->error : "unknown");
+}
+
+int
+kore_pgsql_ntuples(struct kore_pgsql *pgsql)
+{
+ return (PQntuples(pgsql->result));
+}
+
+char *
+kore_pgsql_getvalue(struct kore_pgsql *pgsql, int row, int col)
+{
+ return (PQgetvalue(pgsql->result, row, col));
+}
+
+static int
+pgsql_conn_create(struct http_request *req, int idx)
+{
+ struct pgsql_conn *conn;
+
+ if (pgsql_conn_string == NULL)
+ fatal("pgsql_conn_create: no connection string");
+
+ pgsql_conn_count++;
+ conn = kore_malloc(sizeof(*conn));
+ kore_debug("pgsql_conn_create(): %p", conn);
+ memset(conn, 0, sizeof(*conn));
+
+ conn->db = PQconnectdb(pgsql_conn_string);
+ if (conn->db == NULL || (PQstatus(conn->db) != CONNECTION_OK)) {
+ req->pgsql[idx]->state = KORE_PGSQL_STATE_ERROR;
+ req->pgsql[idx]->error = kore_strdup(PQerrorMessage(conn->db));
+ pgsql_conn_cleanup(conn);
+ return (KORE_RESULT_ERROR);
+ }
+
+ conn->job = NULL;
+ conn->flags = PGSQL_CONN_FREE;
+ conn->type = KORE_TYPE_PGSQL_CONN;
+ TAILQ_INSERT_TAIL(&pgsql_conn_free, conn, list);
+
+ return (KORE_RESULT_OK);
+}
+
+static void
+pgsql_conn_cleanup(struct pgsql_conn *conn)
+{
+ int i;
+ struct http_request *req;
+
+ kore_debug("pgsql_conn_cleanup(): %p", conn);
+
+ if (conn->flags & PGSQL_CONN_FREE)
+ TAILQ_REMOVE(&pgsql_conn_free, conn, list);
+
+ if (conn->job) {
+ i = conn->job->idx;
+ req = conn->job->req;
+
+ req->pgsql[i]->conn = NULL;
+ req->pgsql[i]->state = KORE_PGSQL_STATE_ERROR;
+ req->pgsql[i]->error = kore_strdup(PQerrorMessage(conn->db));
+ req->flags &= ~HTTP_REQUEST_SLEEPING;
+
+ kore_mem_free(conn->job->query);
+ kore_mem_free(conn->job);
+ conn->job = NULL;
+ }
+
+ if (conn->db != NULL)
+ PQfinish(conn->db);
+
+ pgsql_conn_count--;
+ kore_mem_free(conn);
+}
+
+static void
+pgsql_read_result(struct http_request *req, int i, struct pgsql_conn *conn)
+{
+ if (PQisBusy(conn->db)) {
+ req->pgsql[i]->state = KORE_PGSQL_STATE_WAIT;
+ return;
+ }
+
+ req->pgsql[i]->result = PQgetResult(conn->db);
+ if (req->pgsql[i]->result == NULL) {
+ req->pgsql[i]->state = KORE_PGSQL_STATE_DONE;
+ return;
+ }
+
+ switch (PQresultStatus(req->pgsql[i]->result)) {
+ case PGRES_COPY_OUT:
+ case PGRES_COPY_IN:
+ case PGRES_NONFATAL_ERROR:
+ case PGRES_COPY_BOTH:
+ break;
+ case PGRES_COMMAND_OK:
+ req->pgsql[i]->state = KORE_PGSQL_STATE_DONE;
+ break;
+ case PGRES_TUPLES_OK:
+ case PGRES_SINGLE_TUPLE:
+ req->pgsql[i]->state = KORE_PGSQL_STATE_RESULT;
+ break;
+ case PGRES_EMPTY_QUERY:
+ case PGRES_BAD_RESPONSE:
+ case PGRES_FATAL_ERROR:
+ req->pgsql[i]->state = KORE_PGSQL_STATE_ERROR;
+ req->pgsql[i]->error =
+ kore_strdup(PQresultErrorMessage(req->pgsql[i]->result));
+ break;
+ }
+}
diff --git a/src/tasks.c b/src/tasks.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2014 Joris Vink <joris@coders.se>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "kore.h"
+#include "http.h"
+#include "kore_tasks.h"
+
+static u_int8_t threads;
+static pthread_mutex_t task_thread_lock;
+
+static TAILQ_HEAD(, kore_task_thread) task_threads;
+
+static void *task_thread(void *);
+static void task_channel_read(int, void *, u_int32_t);
+static void task_channel_write(int, void *, u_int32_t);
+static void task_thread_spawn(struct kore_task_thread **);
+
+#define THREAD_FD_ASSIGN(t, f, i, o) \
+ do { \
+ if (pthread_self() == t) { \
+ f = i; \
+ } else { \
+ f = o; \
+ } \
+ } while (0);
+
+void
+kore_task_init(void)
+{
+ threads = 0;
+
+ TAILQ_INIT(&task_threads);
+ pthread_mutex_init(&task_thread_lock, NULL);
+}
+
+void
+kore_task_create(struct kore_task **out, int (*entry)(struct kore_task *))
+{
+ struct kore_task *t;
+
+ t = kore_malloc(sizeof(struct kore_task));
+
+ t->req = NULL;
+ t->entry = entry;
+ t->type = KORE_TYPE_TASK;
+ t->state = KORE_TASK_STATE_CREATED;
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0,t->fds) == -1)
+ fatal("kore_task_create: socketpair() %s", errno_s);
+
+ if (out != NULL)
+ *out = t;
+}
+
+void
+kore_task_run(struct kore_task *t)
+{
+ struct kore_task_thread *tt;
+
+ pthread_mutex_lock(&task_thread_lock);
+ if (TAILQ_EMPTY(&task_threads))
+ task_thread_spawn(&tt);
+ else
+ tt = TAILQ_FIRST(&task_threads);
+
+ pthread_mutex_unlock(&task_thread_lock);
+ pthread_mutex_lock(&(tt->lock));
+
+ t->thread = tt;
+ TAILQ_INSERT_TAIL(&(tt->tasks), t, list);
+
+ pthread_mutex_unlock(&(tt->lock));
+ pthread_cond_signal(&(tt->cond));
+}
+
+void
+kore_task_bind_request(struct kore_task *t, struct http_request *req)
+{
+ kore_debug("kore_task_bind_request: %p bound to %p", req, t);
+
+ t->req = req;
+ req->task = t;
+ req->flags |= HTTP_REQUEST_SLEEPING;
+
+ kore_platform_schedule_read(t->fds[0], t);
+}
+
+void
+kore_task_destroy(struct kore_task *t)
+{
+ kore_debug("kore_task_destroy: %p", t);
+
+ if (t->req != NULL)
+ t->req->task = NULL;
+
+ close(t->fds[0]);
+ close(t->fds[1]); /* This might already be closed. */
+
+ kore_mem_free(t);
+}
+
+int
+kore_task_finished(struct kore_task *t)
+{
+ if (t->state == KORE_TASK_STATE_FINISHED)
+ return (1);
+
+ return (0);
+}
+
+void
+kore_task_finish(struct kore_task *t)
+{
+ kore_debug("kore_task_finished: %p (%d)", t, t->result);
+ close(t->fds[1]);
+}
+
+void
+kore_task_channel_write(struct kore_task *t, void *data, u_int32_t len)
+{
+ int fd;
+
+ kore_debug("kore_task_channel_write: %p <- %p (%ld)", t, data, len);
+
+ THREAD_FD_ASSIGN(t->thread->tid, fd, t->fds[1], t->fds[0]);
+ task_channel_write(fd, &len, sizeof(len));
+ task_channel_write(fd, data, len);
+}
+
+u_int32_t
+kore_task_channel_read(struct kore_task *t, void *out, u_int32_t len)
+{
+ int fd;
+ u_int32_t dlen, bytes;
+
+ kore_debug("kore_task_channel_read: %p -> %p (%ld)", t, out, len);
+
+ THREAD_FD_ASSIGN(t->thread->tid, fd, t->fds[1], t->fds[0]);
+ task_channel_read(fd, &dlen, sizeof(dlen));
+
+ if (dlen > len)
+ bytes = len;
+ else
+ bytes = dlen;
+
+ task_channel_read(fd, out, bytes);
+
+ return (dlen);
+}
+
+void
+kore_task_handle(struct kore_task *t, int finished)
+{
+ kore_debug("kore_task_handle: %p, %d", t, finished);
+
+ if (finished) {
+ t->state = KORE_TASK_STATE_FINISHED;
+ if (t->req != NULL) {
+ t->req->flags &= ~HTTP_REQUEST_SLEEPING;
+ if (t->req->flags & HTTP_REQUEST_DELETE)
+ kore_task_destroy(t);
+ }
+ }
+}
+
+static void
+task_channel_write(int fd, void *data, u_int32_t len)
+{
+ ssize_t r;
+ u_int8_t *d;
+ u_int32_t offset;
+
+ d = data;
+ offset = 0;
+ while (offset != len) {
+ r = write(fd, d + offset, len - offset);
+ if (r == -1 && errno == EINTR)
+ continue;
+ if (r == -1)
+ fatal("task_channel_write: %s", errno_s);
+ offset += r;
+ }
+}
+
+static void
+task_channel_read(int fd, void *out, u_int32_t len)
+{
+ ssize_t r;
+ u_int8_t *d;
+ u_int32_t offset;
+
+ d = out;
+ offset = 0;
+ while (offset != len) {
+ r = read(fd, d + offset, len - offset);
+ if (r == -1 && errno == EINTR)
+ continue;
+ if (r == -1)
+ fatal("task_channel_read: %s", errno_s);
+ if (r == 0)
+ fatal("task_channel_read: unexpected eof");
+
+ offset += r;
+ }
+}
+
+static void
+task_thread_spawn(struct kore_task_thread **out)
+{
+ struct kore_task_thread *tt;
+
+ tt = kore_malloc(sizeof(*tt));
+ tt->idx = threads++;
+
+ TAILQ_INIT(&(tt->tasks));
+ pthread_cond_init(&(tt->cond), NULL);
+ pthread_mutex_init(&(tt->lock), NULL);
+
+ if (pthread_create(&(tt->tid), NULL, task_thread, tt) != 0)
+ fatal("pthread_create: %s", errno_s);
+
+ *out = tt;
+}
+
+static void *
+task_thread(void *arg)
+{
+ struct kore_task *t;
+ struct kore_task_thread *tt = arg;
+
+ kore_debug("task_thread: #%d starting", tt->idx);
+
+ pthread_mutex_lock(&(tt->lock));
+
+ pthread_mutex_lock(&task_thread_lock);
+ TAILQ_INSERT_TAIL(&task_threads, tt, list);
+ pthread_mutex_unlock(&task_thread_lock);
+
+ for (;;) {
+ if (TAILQ_EMPTY(&(tt->tasks)))
+ pthread_cond_wait(&(tt->cond), &(tt->lock));
+
+ kore_debug("task_thread#%d: woke up", tt->idx);
+
+ t = TAILQ_FIRST(&(tt->tasks));
+ TAILQ_REMOVE(&(tt->tasks), t, list);
+ pthread_mutex_unlock(&(tt->lock));
+
+ pthread_mutex_lock(&task_thread_lock);
+ TAILQ_REMOVE(&task_threads, tt, list);
+ pthread_mutex_unlock(&task_thread_lock);
+
+ kore_debug("task_thread#%d: executing %p", tt->idx, t);
+
+ t->state = KORE_TASK_STATE_RUNNING;
+ t->result = t->entry(t);
+ kore_task_finish(t);
+
+ pthread_mutex_lock(&task_thread_lock);
+ TAILQ_INSERT_HEAD(&task_threads, tt, list);
+ pthread_mutex_unlock(&task_thread_lock);
+
+ pthread_mutex_lock(&(tt->lock));
+ }
+
+ pthread_exit(NULL);
+
+ /* NOTREACHED */
+ return (NULL);
+}