diff --git a/LICENSE b/LICENSE
index 1d5d86b9d4a221675ecf7e6f091720d8496bca72..58c0cabfa3967b7afdb598d73c15bf5b58c3eb82 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright 2020
+Copyright 2020 to Lektor contributors
 
 Permission to use, copy, modify, and/or distribute this software for any purpose
 with or without fee is hereby granted, provided that the above copyright notice
diff --git a/inc/common/common.h b/inc/common/common.h
new file mode 100644
index 0000000000000000000000000000000000000000..09d87d2d509af17bb1e02fa8f0ea9ab8a80dd553
--- /dev/null
+++ b/inc/common/common.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include <common/define.h>
+#include <common/macro.h>
+
+#define not_implemented() __not_implemented(__func__,__FILE__,__LINE__)
+extern void __not_implemented(const char *func, char *file, int line);
diff --git a/inc/common/define.h b/inc/common/define.h
new file mode 100644
index 0000000000000000000000000000000000000000..3fd65003f2c1f471413b237d3729e0936506567e
--- /dev/null
+++ b/inc/common/define.h
@@ -0,0 +1,9 @@
+#pragma once
+
+/* Max value for any buffer, to not squash the stack. */
+#define BUFFER_MAX 4096
+
+#ifndef __GNUC__
+#define inline
+#endif
+
diff --git a/inc/common/macro.h b/inc/common/macro.h
new file mode 100644
index 0000000000000000000000000000000000000000..b2b3340067b940ff9498ab4449c27025fcb05fe4
--- /dev/null
+++ b/inc/common/macro.h
@@ -0,0 +1,53 @@
+#pragma once
+
+#define BRACKETS_THAT(that) (that)
+
+#ifndef MAX
+#define MAX(a, b) ((a) < (b) ? (b) : (a))
+#endif /* MAX */
+
+#ifndef MIN
+#define MIN(a, b) ((a) > (b) ? (b) : (a))
+#endif /* MIN */
+
+#define RETURN_IF(cond, msg, ret)                                       \
+    if (cond) {                                                         \
+        LOG_ERROR("%s", msg);                                           \
+        return ret;                                                     \
+    }
+#define GOTO_IF(cond, msg, label)                                       \
+    if (cond) {                                                         \
+        LOG_ERROR("%s", msg);                                           \
+        goto label;                                                     \
+    }
+
+#define GOTO_UNLESS(cond, msg, label)   GOTO_IF(!(cond), msg, label)
+#define RETURN_UNLESS(cond, msg, ret)   RETURN_IF(!(cond), msg, ret)
+#define NOTHING                         /* Usefull to return nothing. */
+
+#define STRTOL(ret, str, endptr, err_flag)                              \
+{                                                                       \
+    err_flag = 0;                                                       \
+    errno    = 0;                                                       \
+    ret      = str == NULL ? 0 : strtol(str, &(endptr), 0);             \
+    err_flag = errno != 0 || endptr == str;                             \
+}
+
+#define ERROR   1
+#define WARN    2
+#define INFO    3
+#define DEBUG   4
+#define LOG(format, level, section, ...)                                            \
+    fprintf(stderr, " %c %s%s: " format "\n",                                       \
+            level == ERROR ? '!' : level == WARN ? '*' : level == INFO ? '.' : ' ', \
+            sizeof(section) > sizeof("") ? ("[" section "] ") : "",                 \
+            __func__,                                                               \
+            __VA_ARGS__)
+#define LOG_INFO_SCT(section, format, ...)      LOG(format, INFO, section, __VA_ARGS__)
+#define LOG_WARN_SCT(section, format, ...)      LOG(format, WARN, section, __VA_ARGS__)
+#define LOG_ERROR_SCT(section, format, ...)     LOG(format, ERROR, section, __VA_ARGS__)
+#define LOG_DEBUG_SCT(section, format, ...)     LOG(format, DEBUG, section, __VA_ARGS__)
+#define LOG_INFO(format, ...)                   LOG_INFO_SCT("", format, __VA_ARGS__)
+#define LOG_WARN(format, ...)                   LOG_WARN_SCT("", format, __VA_ARGS__)
+#define LOG_ERROR(format, ...)                  LOG_DEBUG_SCT("", format, __VA_ARGS__)
+#define LOG_DEBUG(format, ...)                  LOG_DEBUG_SCT("", format, __VA_ARGS__)
diff --git a/inc/lektor/bufferfd.h b/inc/lektor/bufferfd.h
index 52649e158a55d45ea763f6be220800f6d00e8f85..97bc0237268efdc33f5757032c108be91187dc35 100644
--- a/inc/lektor/bufferfd.h
+++ b/inc/lektor/bufferfd.h
@@ -23,7 +23,7 @@
 
 #pragma once
 
-#include <lektor/defines.h>
+#include <lektor/define.h>
 #include <stdint.h>
 #include <stdlib.h>
 
diff --git a/inc/lektor/cmd.h b/inc/lektor/cmd.h
index 98817754a011d9eae462de1cd8520880c3c0adc8..3ac6c3ad47698fa9996cce8320b3462ce945bc5e 100644
--- a/inc/lektor/cmd.h
+++ b/inc/lektor/cmd.h
@@ -1,6 +1,6 @@
 #pragma once
 
-#include <lektor/defines.h>
+#include <lektor/define.h>
 #include <stdnoreturn.h>
 #include <stddef.h>
 
diff --git a/inc/lektor/commands.h b/inc/lektor/commands.h
index 7745fd4c1ae99a866e1c3fa43935d06bc97c04b2..eea73378372ddb9af80a5dab9e96d2c2211f7c70 100644
--- a/inc/lektor/commands.h
+++ b/inc/lektor/commands.h
@@ -1,6 +1,6 @@
 #pragma once
 
-#include <lektor/defines.h>
+#include <lektor/define.h>
 #include <lektor/net.h>
 #include <lektor/window.h>
 
diff --git a/inc/lektor/config.h b/inc/lektor/config.h
index 75aaced78226f816388555dae0994c879fafdb93..fc013160204b3dd3f45f7fc5ff0aa9789a38da8a 100644
--- a/inc/lektor/config.h
+++ b/inc/lektor/config.h
@@ -1,6 +1,6 @@
 #pragma once
 
-#include <lektor/defines.h>
+#include <lektor/define.h>
 #include <stddef.h>
 #include <sqlite3.h>
 
diff --git a/inc/lektor/database.h b/inc/lektor/database.h
index ca761d847d9afa98b8b339b614a3607279f44c98..23148fa739e0ef3fe4b2eb2e36cf4be4e1a5c0cc 100644
--- a/inc/lektor/database.h
+++ b/inc/lektor/database.h
@@ -1,6 +1,6 @@
 #pragma once
 
-#include <lektor/defines.h>
+#include <lektor/define.h>
 #include <lektor/mkv.h>
 #include <lektor/uri.h>
 
diff --git a/inc/lektor/defines.h b/inc/lektor/define.h
similarity index 97%
rename from inc/lektor/defines.h
rename to inc/lektor/define.h
index 8d71316b3d8d499888b1ccc9d1d31ce98e82b21c..d68144f42a19e9dae0c1378489aa92e005361c8e 100644
--- a/inc/lektor/defines.h
+++ b/inc/lektor/define.h
@@ -1,8 +1,9 @@
 #pragma once
 
+#include <common/define.h>
+
 #define LKT_MAX_SQLITE_STATEMENT    1024
 #define PROTECTED_DATABASE          "disk"
-#define BUFFER_MAX                  4096
 
 #define LKT_DATABASE_NAME_KID           "id"
 #define LKT_DATABASE_NAME_KNAME         "source_name"
diff --git a/inc/lektor/macro.h b/inc/lektor/macro.h
index 5009c65ad50a3154bac1bd14561358fd8082ce88..48c66716fec43acae1c01471611bcd0c95a87113 100644
--- a/inc/lektor/macro.h
+++ b/inc/lektor/macro.h
@@ -1,37 +1,38 @@
 #pragma once
+#include <common/macro.h>
 
 #define SQLITE_PREPARE(db, stmt, SQL, goto_label)                       \
     if (sqlite3_prepare_v2(db, SQL, -1, &(stmt), 0) != SQLITE_OK) {     \
-        fprintf(stderr, " ! %s: Failed to prepare statement: %s\n",     \
-                __func__, sqlite3_errmsg(db));                          \
+        LOG_ERROR_SCT("DB", "Failed to prepare statement: %s",          \
+                      sqlite3_errmsg(db));                              \
         goto goto_label;                                                \
     }
 
 #define SQLITE_EXEC(db, SQL, goto_label)                                \
     if (sqlite3_exec(db, SQL, NULL, NULL, NULL) != SQLITE_OK) {         \
-        fprintf(stderr, " ! %s: Failed to exec statement: %s\n",        \
-                __func__, sqlite3_errmsg(db));                          \
+        LOG_ERROR_SCT("DB", "Failed to exec statement: %s",             \
+                      sqlite3_errmsg(db));                              \
         goto goto_label;                                                \
     }
 
 #define SQLITE_BIND_TEXT(db, stmt, pos, text, error)                    \
     if (sqlite3_bind_text(stmt, pos, text, -1, 0) != SQLITE_OK) {       \
-        fprintf(stderr, " ! %s: Failed to bind text %s at position"     \
-                "%d: %s\n", __func__, text, pos, sqlite3_errmsg(db));   \
+        LOG_ERROR_SCT("DB", "Failed to bind text %s at pos %d: %s",     \
+                      text, pos, sqlite3_errmsg(db));                   \
         goto error;                                                     \
     }
 
 #define SQLITE_BIND_INT(db, stmt, pos, integer, error)                  \
     if (sqlite3_bind_int(stmt, pos, integer) != SQLITE_OK) {            \
-        fprintf(stderr, " ! %s: Failed to bind int %d at position %d:"  \
-                "%s\n", __func__, integer, pos, sqlite3_errmsg(db));    \
+        LOG_ERROR_SCT("DB", "Failed to bind int %d at pos %d: %s",      \
+                      integer, pos, sqlite3_errmsg(db));                \
         goto error;                                                     \
     }
 
 #define SQLITE_STEP(db, stmt, code, error)                              \
     if (sqlite3_step(stmt) != code) {                                   \
-        fprintf(stderr, " ! %s: Failed to step and get a row: %s\n",    \
-                __func__, sqlite3_errmsg(db));                          \
+        LOG_ERROR_SCT("DB", "Failed to step and get a row: %s",         \
+                      sqlite3_errmsg(db));                              \
         goto error;                                                     \
     }
 
@@ -41,34 +42,3 @@
 
 #define SQLITE_DO_ROLLBACK(db)                                          \
     sqlite3_exec(db, "ROLLBACK TRANSACTION;\n", NULL, NULL, NULL);
-
-#ifndef MAX
-#define MAX(a, b) ((a) < (b) ? (b) : (a))
-#endif /* MAX */
-
-#ifndef MIN
-#define MIN(a, b) ((a) > (b) ? (b) : (a))
-#endif /* MIN */
-
-#define RETURN_IF(cond, msg, ret)                                       \
-    if (cond) {                                                         \
-        fprintf(stderr, " ! %s: %s\n", __func__, msg);                  \
-        return ret;                                                     \
-    }
-#define GOTO_IF(cond, msg, label)                                       \
-    if (cond) {                                                         \
-        fprintf(stderr, " ! %s: %s\n", __func__, msg);                  \
-        goto label;                                                     \
-    }
-
-#define GOTO_UNLESS(cond, msg, label)   GOTO_IF(!(cond), msg, label)
-#define RETURN_UNLESS(cond, msg, ret)   RETURN_IF(!(cond), msg, ret)
-#define NOTHING                         /* Usefull to return nothing. */
-
-#define STRTOL(ret, str, endptr, err_flag)                              \
-{                                                                       \
-    err_flag = 0;                                                       \
-    errno    = 0;                                                       \
-    ret      = str == NULL ? 0 : strtol(str, &(endptr), 0);             \
-    err_flag = errno != 0 || endptr == str;                             \
-}
diff --git a/inc/lektor/mkv.h b/inc/lektor/mkv.h
index 447a58eee6455cbcc44d5290a5b6786d31ad8295..681315934b96e0b0b5557ef0dbaaeea50d20f71b 100644
--- a/inc/lektor/mkv.h
+++ b/inc/lektor/mkv.h
@@ -23,7 +23,7 @@
  */
 #pragma once
 
-#include <lektor/defines.h>
+#include <lektor/define.h>
 #include <linux/limits.h>
 #include <stddef.h>
 
diff --git a/inc/lektor/module/module_sdl2.h b/inc/lektor/module/module_sdl2.h
index 8caa155f641e8196519d9d3ff5c415778ab99cba..e4a2dd2c8c602baa453b69c8e20233f6e9cbd1e1 100644
--- a/inc/lektor/module/module_sdl2.h
+++ b/inc/lektor/module/module_sdl2.h
@@ -1,6 +1,6 @@
 #pragma once
 
-#include <lektor/defines.h>
+#include <lektor/define.h>
 #include <stdbool.h>
 #include <sqlite3.h>
 #include <lektor/window.h>
diff --git a/inc/lektor/module/module_x11.h b/inc/lektor/module/module_x11.h
index 29f943a99fdc43eec72ff7fa91e3468665124700..880452463c30b72ac7f1e6405a91abf92a6df7ba 100644
--- a/inc/lektor/module/module_x11.h
+++ b/inc/lektor/module/module_x11.h
@@ -1,6 +1,6 @@
 #pragma once
 
-#include <lektor/defines.h>
+#include <lektor/define.h>
 #include <stdbool.h>
 #include <sqlite3.h>
 #include <lektor/window.h>
diff --git a/inc/lektor/net.h b/inc/lektor/net.h
index 57ae3b3bcdf35e2dda7daf2214c2284cfefc77a4..e25534f5dd589694bd0c90c42a6d9f7816f6b1ab 100644
--- a/inc/lektor/net.h
+++ b/inc/lektor/net.h
@@ -1,7 +1,7 @@
 #pragma once
 
-#include <lektor/defines.h>
 #include <lektor/repo.h>
+#include <lektor/define.h>
 #include <lektor/config.h>
 #include <lektor/window.h>
 #include <sqlite3.h>
diff --git a/inc/lektor/repo.h b/inc/lektor/repo.h
index 55c38acd007d2dfce2ab1b5f8886002ec8183789..88059cb1466c6622c5b0219e9871febe69bacd65 100644
--- a/inc/lektor/repo.h
+++ b/inc/lektor/repo.h
@@ -1,6 +1,6 @@
 #pragma once
 
-#include <lektor/defines.h>
+#include <lektor/define.h>
 #include <lektor/mkv.h>
 #include <lektor/thread.h>
 #include <lektor/uri.h>
@@ -22,7 +22,7 @@ struct lkt_repo {
     const uint64_t version;
 
     /* Thread related */
-    struct lkt_thread self;
+    struct poller_thread self;
     volatile int init;
     volatile int stop;
     volatile int all_json;
diff --git a/inc/lektor/thread.h b/inc/lektor/thread.h
index 553333b8690ba4b090c9ee1af3d5db6d89fe66da..dcd3d75c23ede39f42f314ea098e04c1bb1a812b 100644
--- a/inc/lektor/thread.h
+++ b/inc/lektor/thread.h
@@ -1,10 +1,10 @@
 #pragma once
 
-#include <lektor/defines.h>
+#include <lektor/define.h>
 #include <pthread.h>
 #include <sys/types.h>
 
-struct lkt_thread {
+struct poller_thread {
     /* The input poll. */
     volatile unsigned int input_len;
     volatile unsigned int input_size;
@@ -21,25 +21,25 @@ struct lkt_thread {
     pthread_mutex_t output_lock;
 };
 
-struct lkt_thread_arg {
-    struct lkt_thread *self;
+struct poller_thread_arg {
+    struct poller_thread *self;
     void *args;
 };
 
 
 /* Create a new thread. Returns 0 on success, a non zero value on error. */
-int lkt_th_new(struct lkt_thread *th, unsigned int init_sizes,
-               void *(*func)(struct lkt_thread_arg *), void *args);
+int lkt_th_new(struct poller_thread *th, unsigned int init_sizes,
+               void *(*func)(struct poller_thread_arg *), void *args);
 
 /* Joins a thread. Returns 0 on success, a non zero value on error. */
-int lkt_th_join(struct lkt_thread *th, void **ret);
+int lkt_th_join(struct poller_thread *th, void **ret);
 
 /* Input qeueu manipulations. */
-int lkt_th_append_input(struct lkt_thread *th, void *ptr);
-int lkt_th_pop_input(struct lkt_thread *th, void **ptr);
-int lkt_th_head_input(struct lkt_thread *th, void **ptr);
+int lkt_th_append_input(struct poller_thread *th, void *ptr);
+int lkt_th_pop_input(struct poller_thread *th, void **ptr);
+int lkt_th_head_input(struct poller_thread *th, void **ptr);
 
 /* Output qeueu manipulations. */
-int lkt_th_append_output(struct lkt_thread *th, void *ptr);
-int lkt_th_pop_output(struct lkt_thread *th, void **ptr);
-int lkt_th_head_output(struct lkt_thread *th, void **ptr);
+int lkt_th_append_output(struct poller_thread *th, void *ptr);
+int lkt_th_pop_output(struct poller_thread *th, void **ptr);
+int lkt_th_head_output(struct poller_thread *th, void **ptr);
diff --git a/inc/lektor/uri.h b/inc/lektor/uri.h
index 25d7a147b6d34cc05f202651caedb331ab31c5dd..0c4cff92f74681da6fc13d49671be8ed7f3c270e 100644
--- a/inc/lektor/uri.h
+++ b/inc/lektor/uri.h
@@ -1,6 +1,6 @@
 #pragma once
 
-#include <lektor/defines.h>
+#include <lektor/define.h>
 #include <stddef.h>
 #include <stddef.h>
 #include <stdbool.h>
diff --git a/inc/lektor/utils.h b/inc/lektor/utils.h
index 27755675b6499a30f388a97e1cb84bd20a0ddbe2..aa175cc8f40d0410a82acdd9677487e98a7edfb3 100644
--- a/inc/lektor/utils.h
+++ b/inc/lektor/utils.h
@@ -1,7 +1,7 @@
 /* Random things. */
 #pragma once
 
-#include <lektor/defines.h>
+#include <lektor/define.h>
 #include <stdint.h>
 #include <stdlib.h>
 
diff --git a/inc/lektor/window.h b/inc/lektor/window.h
index 87644c98f139d81ce4c35d8b36dd21019926d467..35d87fbccbab09cdc17f8537d9b87527dc7b0975 100644
--- a/inc/lektor/window.h
+++ b/inc/lektor/window.h
@@ -1,6 +1,6 @@
 #pragma once
 
-#include <lektor/defines.h>
+#include <lektor/define.h>
 #include <stdbool.h>
 #include <sqlite3.h>
 
diff --git a/inc/mthread/mthread.h b/inc/mthread/mthread.h
new file mode 100644
index 0000000000000000000000000000000000000000..c708a4414d54bce351aaa564ee4a5b4da46c9f2d
--- /dev/null
+++ b/inc/mthread/mthread.h
@@ -0,0 +1,109 @@
+#pragma once
+
+/* Types */
+typedef volatile unsigned int mthread_tst_t;
+
+struct mthread_list_s;
+typedef struct mthread_list_s mthread_list_t;
+
+struct mthread_s;
+typedef struct mthread_s *mthread_t;
+
+struct mthread_attr_s;
+typedef struct mthread_attr_s mthread_attr_t;
+
+struct mthread_mutex_s {
+    volatile int nb_thread;
+    mthread_tst_t lock;
+    mthread_list_t *list;   /* lock management already handled */
+};
+typedef struct mthread_mutex_s mthread_mutex_t;
+
+#define MTHREAD_MUTEX_INITIALIZER { .nb_thread = 0, .lock = 0, .list = NULL }
+
+struct mthread_mutexattr_s;
+typedef struct mthread_mutexattr_s mthread_mutexattr_t;
+
+struct mthread_cond_s {
+    mthread_tst_t lock;         /* Protects the structure.  */
+    mthread_list_t *list;       /* List of waiting threads. */
+};
+typedef struct mthread_cond_s mthread_cond_t;
+
+struct mthread_condattr_s;
+typedef struct mthread_condattr_s mthread_condattr_t;
+
+typedef unsigned int mthread_key_t;
+
+typedef mthread_tst_t mthread_once_t;
+
+struct mthread_sem_s {
+    volatile unsigned int value;    /* Number of wait that are done at the same time. */
+    mthread_mutex_t mutex;          /* Protect max_permit.                            */
+};
+typedef struct mthread_sem_s mthread_sem_t;
+
+/* Function for handling threads.  */
+
+int mthread_create(mthread_t *__threadp, const mthread_attr_t *__attr, void *(*__start_routine) (void *),
+                   void *__arg);
+
+mthread_t mthread_self(void);
+int mthread_equal(mthread_t __thread1, mthread_t __thread2);
+void mthread_exit(void *__retval);
+int mthread_join(mthread_t __th, void **__thread_return);
+
+/* Functions for mutex handling.  */
+
+int mthread_mutex_init (mthread_mutex_t *__mutex,
+                        const mthread_mutexattr_t *__mutex_attr);
+int mthread_mutex_destroy(mthread_mutex_t *__mutex);
+int mthread_mutex_trylock(mthread_mutex_t *__mutex);
+int mthread_mutex_lock(mthread_mutex_t *__mutex);
+int mthread_mutex_unlock(mthread_mutex_t *__mutex);
+
+/* Functions for handling conditional variables.  */
+
+int mthread_cond_init(mthread_cond_t *__cond,
+                      const mthread_condattr_t *__cond_attr);
+int mthread_cond_destroy(mthread_cond_t *__cond);
+int mthread_cond_signal(mthread_cond_t *__cond);
+int mthread_cond_broadcast(mthread_cond_t *__cond);
+int mthread_cond_wait(mthread_cond_t *__cond, mthread_mutex_t *__mutex);
+
+/* Functions for handling thread-specific data.  */
+
+int mthread_key_create(mthread_key_t *__key, void (*__destr_function) (void *));
+int mthread_key_delete(mthread_key_t __key);
+int mthread_setspecific(mthread_key_t __key, const void *__pointer);
+void *mthread_getspecific(mthread_key_t __key);
+
+
+/* Functions for handling initialization.  */
+
+/* Guarantee that the initialization function INIT_ROUTINE will be called
+   only once, even if mthread_once is executed several times with the
+   same ONCE_CONTROL argument. ONCE_CONTROL must point to a static or
+   variable initialized to MTHREAD_ONCE_INIT.
+
+   The initialization functions might throw exception which is why
+   this function is not marked with .  */
+int mthread_once(mthread_once_t *__once_control, void (*__init_routine) (void));
+
+/* Functions for handling semaphore.  */
+
+int mthread_sem_init(mthread_sem_t *sem, unsigned int value);
+int mthread_sem_wait(mthread_sem_t *sem);     /* P(sem), wait(sem) */
+int mthread_sem_post(mthread_sem_t *sem);     /* V(sem), signal(sem) */
+
+int mthread_sem_getvalue(mthread_sem_t *sem, int *sval);
+int mthread_sem_trywait(mthread_sem_t *sem);
+
+int mthread_sem_destroy(mthread_sem_t *sem);  /* undo sem_init() */
+
+void mthread_yield();
+
+/* Initialize mthread. */
+
+void mthread_init(void);
+void *mthread_main(void *arg);
diff --git a/inc/mthread/mthread_internal.h b/inc/mthread/mthread_internal.h
new file mode 100644
index 0000000000000000000000000000000000000000..c4eb661d17602bc96fd4bb81395e9176ae039a26
--- /dev/null
+++ b/inc/mthread/mthread_internal.h
@@ -0,0 +1,69 @@
+#pragma once
+
+#define TWO_LEVEL
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ucontext.h>
+
+#include <mthread/mthread.h>
+#include <common/common.h>
+
+#define MTHREAD_DEFAULT_STACK           BRACKETS_THAT(128*1024) /* 128 kB */
+#define MTHREAD_MAX_VIRUTAL_PROCESSORS  256
+
+struct mthread_list_s {
+    volatile struct mthread_s *first;
+    volatile struct mthread_s *last;
+    mthread_tst_t lock;
+};
+
+typedef struct {
+    struct mthread_s *idle;
+    volatile struct mthread_s *current;
+    mthread_list_t ready_list;
+    int rank;
+    volatile int state;
+    volatile struct mthread_s *resched;
+    volatile mthread_tst_t *p;
+} mthread_virtual_processor_t;
+
+typedef enum { RUNNING, BLOCKED, ZOMBIE } mthread_status_t;
+
+#define INIT_KEYS_LIST 48
+
+struct keys_list {
+    volatile mthread_key_t *list;
+    unsigned int first_avail;
+    unsigned int size;
+};
+
+struct mthread_s {
+    ucontext_t uc;
+    volatile void *res;
+    void *arg;
+    void *(*__start_routine) (void *);
+    volatile struct mthread_s *next;
+    volatile mthread_status_t status;
+    int not_migrable;
+    mthread_virtual_processor_t *vp;
+    void *stack;
+    struct keys_list keys;
+};
+
+#define MTHREAD_LIST_INIT { .first = NULL, .last = NULL, .lock = 0 }
+
+int mthread_test_and_set(mthread_tst_t *atomic);
+void mthread_spinlock_lock(mthread_tst_t *atomic);
+void mthread_spinlock_unlock(mthread_tst_t *atomic);
+int mthread_get_vp_rank();
+
+void *safe_malloc(size_t size);
+
+void mthread_insert_first(struct mthread_s *item, mthread_list_t *list);
+void mthread_insert_last(struct mthread_s *item, mthread_list_t *list);
+struct mthread_s *mthread_remove_first(mthread_list_t *list);
+int mthread_list_test(struct mthread_s *item, mthread_list_t *list);
+
+void __mthread_yield(mthread_virtual_processor_t *vp);
+mthread_virtual_processor_t *mthread_get_vp();
diff --git a/meson.build b/meson.build
index 17e180c6647fa11811685474327fff70f8f6fb46..fe4628f55a254ce660c0db26de983ee4b393e9f3 100644
--- a/meson.build
+++ b/meson.build
@@ -18,6 +18,28 @@ dep_x11 = dependency('x11', required : false)
 dep_mpv = dependency('mpv', required : false)
 dep_sdl = dependency('sdl2', required : false)
 
+## Get architecture
+archi = run_command('uname', '-p').stdout().strip()
+if archi == 'unknown'
+  archi = run_command('uname', '-m').stdout().strip()
+endif
+if archi == 'unknown'
+  archi = run_command('arch').stdout().strip()
+endif
+add_global_arguments('-D' + archi + '_ARCH', language : 'c')
+add_global_arguments('-D_REENTRANT', language : 'c')
+
+## Sources for mthread
+mthread_sources = [ 'src/mthread/mthread.c'
+                  , 'src/mthread/mthread_cond.c'
+                  , 'src/mthread/mthread_debug.c'
+                  , 'src/mthread/mthread_key.c'
+                  , 'src/mthread/mthread_mutex.c'
+                  , 'src/mthread/mthread_once.c'
+                  , 'src/mthread/mthread_sem.c'
+                  , 'src/mthread/mthread_tst.c'
+                  ]
+
 ## Sources for the server
 core_sources =  [ 'src/mkv/bufferfd.c'
                 , 'src/mkv/write.c'
@@ -54,18 +76,29 @@ core_deps = [ dependency('sqlite3', version : '>= 3.31.0')
             , dependency('threads', required : true)
             ]
 
+common_deps = [ declare_dependency( link_with: static_library( 'common'
+                                                             , files('src/common.c')
+                                                             , include_directories : includes )
+                                  , include_directories: includes ) ]
+
+mthread_deps = [ declare_dependency( link_with: static_library( 'mthread'
+                                                              , files(mthread_sources)
+                                                              , include_directories : includes )
+                                   , include_directories: includes
+                                   , dependencies : [ common_deps ] ) ]
+
 lib = static_library( meson.project_name()
                     , files(core_sources)
                     , include_directories : includes
-                    , dependencies : [ core_deps, libdl ]
+                    , dependencies : [ core_deps, libdl, common_deps ]
                     )
 
-bin_deps =  core_deps + [ declare_dependency( link_with: lib, include_directories: includes) ]
+bin_deps = [ declare_dependency( link_with: lib, include_directories: includes) ]
 
 srv = executable( meson.project_name() + 'd'
                 , files('src/main/server.c')
                 , include_directories : includes
-                , dependencies : [ bin_deps ]
+                , dependencies : [ bin_deps, mthread_deps ]
                 , install : true
                 )
 
@@ -89,7 +122,7 @@ if dep_x11.found() and dep_mpv.found()
   lib_mod_x11 = shared_library ( '_module_x11'
                                , files(['src/module/module_x11.c', 'src/module/mpv.c'])
                                , include_directories : includes
-                               , dependencies : [ dep_x11, dep_mpv ]
+                               , dependencies : [ dep_x11, dep_mpv, mthread_deps ]
                                , link_with : lib
                                , install : true
                                , install_dir : 'lib/lektor'
@@ -101,7 +134,7 @@ if dep_sdl.found() and dep_mpv.found()
   lib_mod_sdl = shared_library ( '_module_sdl2'
                                , files(['src/module/module_sdl2.c', 'src/module/mpv.c'])
                                , include_directories : includes
-                               , dependencies : [ dep_sdl, dep_mpv ]
+                               , dependencies : [ dep_sdl, dep_mpv, mthread_deps ]
                                , link_with : lib
                                , install : true
                                , install_dir : 'lib/lektor'
diff --git a/src/cmd.c b/src/cmd.c
index e13fd7674a7bd31792693a0685ccb93a4f99f461..f5c0c61991087bd5e05d4e287b8e191aae8e6435 100644
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -53,7 +53,7 @@ not_found:
         it->call(&arguments);
     }
 
-    fprintf(stderr, "Command '%s' could not be found\n", argv[0]);
+    LOG_ERROR_SCT("COMMAND", "Command '%s' could not be found", argv[0]);
     exit(EXIT_FAILURE);
 
 no_args:
@@ -63,7 +63,7 @@ no_args:
     exit(EXIT_SUCCESS);
 
 not_exclusive:
-    fprintf(stderr, "Failed to determine which option to choose, '%s' is not exclusive\n", argv[0]);
+    LOG_ERROR_SCT("COMMAND", "Failed to determine option, '%s' not exclusive", argv[0]);
     exit(EXIT_FAILURE);
 }
 
diff --git a/src/commands.c b/src/commands.c
index a9f83d66d6689503ee0f111fb20a7cfecdc4219d..e6309f854ab9b1dc6ccfd54bd07e52130792c13a 100644
--- a/src/commands.c
+++ b/src/commands.c
@@ -30,24 +30,24 @@ command_restart(struct lkt_state *srv, size_t c)
     RETURN_UNLESS(lkt_client_auth(srv, c, false), "Failed to authentificate user", false);
 
     if (readlink(SELF_EXECUTABLE_LINUX, exe, PATH_MAX - 1) > 0) {
-        fprintf(stderr, " * Restart lektord\n");
+        LOG_INFO_SCT("GENERAL", "Restart lektord: %s", exe);
         close(srv->fds[0].fd);
         execv(exe, argv);
     }
 
     if (readlink(SELF_EXECUTABLE_FREEBSD, exe, PATH_MAX - 1) > 0) {
-        fprintf(stderr, " * Restart lektord\n");
+        LOG_INFO_SCT("GENERAL", "Restart lektord: %s", exe);
         close(srv->fds[0].fd);
         execv(exe, argv);
     }
 
     if (readlink(SELF_EXECUTABLE_SOLARIS, exe, PATH_MAX - 1) > 0) {
-        fprintf(stderr, " * Restart lektord\n");
+        LOG_INFO_SCT("GENERAL", "Restart lektord: %s", exe);
         close(srv->fds[0].fd);
         execv(exe, argv);
     }
 
-    fprintf(stderr, " ! command_restart: Failed to exec lektor or OS not supported\n");
+    LOG_ERROR_SCT("GENERAL", "%s", "Failed to exec lektor or OS not supported");
     abort();
 }
 
@@ -77,7 +77,7 @@ inline bool
 command_kill(struct lkt_state *srv, size_t c)
 {
     RETURN_UNLESS(lkt_client_auth(srv, c, false), "Failed to authentificate user", false);
-    fprintf(stderr, " * Stopping lektord\n");
+    LOG_INFO_SCT("GENERAL", "%s", "Stopping lektord");
     close(srv->fds[0].fd);
     exit(EXIT_SUCCESS);
 }
@@ -92,7 +92,7 @@ command_currentsong(struct lkt_state *srv, size_t c)
     memset(&kara, 0, sizeof(struct kara_metadata));
 
     if (!database_queue_current_kara(srv->db, &kara, NULL))
-        fprintf(stderr, " . command_currentsong: Failed to get information about the current kara\n");
+        LOG_ERROR_SCT("COMMAND", "%s", "Failed to get information about the current kara");
 
     out = lkt_message_new();
     idx = snprintf(out->data, LKT_MESSAGE_MAX,
@@ -421,7 +421,7 @@ command_idle(struct lkt_state *srv, size_t c, struct lkt_command *cmd)
     if (!once)
         *clt_mask = MPD_IDLE_ALL;
 
-    fprintf(stderr, " * command_idle: idle mask for client number %ld is 0x%X\n", c, *clt_mask);
+    LOG_INFO_SCT("COMMAND", "Idle mask for client number %ld is 0x%X", c, *clt_mask);
 
     return true;
 }
@@ -612,7 +612,7 @@ command_set_playback_option(struct lkt_state *srv, size_t c, enum lkt_playback_o
         break;
     case lkt_playback_option_volume:
         ret = database_config_queue(srv->db, "volume", val);
-        fprintf(stderr, " . command_set_playback_option: set volume to %ld\n", val);
+        LOG_INFO_SCT("COMMAND", "Set volume to %ld", val);
         ret &= win->set_volume(win, val);
         srv->mpd_idle_events |= MPD_IDLE_MIXER;
         break;
@@ -650,12 +650,12 @@ command_plt_add(sqlite3 *db, char *args[LKT_MESSAGE_ARGS_MAX], enum mpd_idle_fla
 
     else {
         if (!lkt_uri_from(&uri, args[1])) {
-            fprintf(stderr, " ! command_plt_add: Failed to get uri\n");
+            LOG_ERROR_SCT("COMMAND", "%s", "Failed to get uri");
             goto end_plt_add_uri;
         }
 
         else if (!database_plt_add_uri(db, args[0], &uri)) {
-            fprintf(stderr, " ! command_plt_add: Failed to add uri to playlist\n");
+            LOG_ERROR_SCT("COMMAND", "Failed to add uri '%s' to playlist", args[1]);
             goto end_plt_add_uri;
         }
 
@@ -717,7 +717,7 @@ command_plt_export(sqlite3 *db, char *args[LKT_MESSAGE_ARGS_MAX], enum mpd_idle_
     RETURN_UNLESS(database_attach(db, args[0], args[1]), "Failed to attach database", false);
     RETURN_UNLESS(database_plt_export(db, args[0]), "Failed to export playlist", false);
     RETURN_UNLESS(database_detach(db, args[0]), "Failed to detach database", false);
-    fprintf(stderr, " * Exported playlist %s with path '%s'\n", args[0], args[1]);
+    LOG_INFO_SCT("COMMAND", "Exported playlist %s with path '%s'", args[0], args[1]);
     *watch_mask_ptr |= MPD_IDLE_PLAYLIST;
     return true;
 }
@@ -729,7 +729,7 @@ command_plt_import(sqlite3 *db, char *args[LKT_MESSAGE_ARGS_MAX], enum mpd_idle_
     RETURN_UNLESS(database_attach(db, args[0], args[1]), "Failed to attach database", false);
     RETURN_UNLESS(database_plt_import(db, args[0]), "Failed to import playlist", false);
     RETURN_UNLESS(database_detach(db, args[0]), "Failed to detach playlist", false);
-    fprintf(stderr, " * Imported playlist %s with path '%s'\n", args[0], args[1]);
+    LOG_INFO_SCT("COMMAND", "Imported playlist %s with path '%s'", args[0], args[1]);
     *watch_mask_ptr |= MPD_IDLE_PLAYLIST;
     return true;
 }
@@ -795,10 +795,10 @@ command_queue_list(struct lkt_state *srv, size_t c, char *args[LKT_MESSAGE_ARGS_
         to = labs(val);
 
         if (to < from) {
-            fprintf(stderr, " . command_queue_list: switch range values, wrong order\n");
             tmp_switch = to;
             to = from;
             from = tmp_switch;
+            LOG_INFO_SCT("COMMAND", "Switch range values wrong order, now %d:%d", from, to);
         }
 
         goto is_a_range;
@@ -1016,7 +1016,7 @@ list_stickers_check_value:
     }
 
 unknown:
-    fprintf(stderr, " . command_sticker_list: Specified command is invalid or unknown\n");
+    LOG_ERROR_SCT("COMMAND", "%s", "Specified command is invalid or unknown");
     return false;
 }
 
diff --git a/src/common.c b/src/common.c
new file mode 100644
index 0000000000000000000000000000000000000000..f0195a6c88ec9b2660bfd091499dceab267044de
--- /dev/null
+++ b/src/common.c
@@ -0,0 +1,12 @@
+#include <common/common.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+void
+__not_implemented(const char *func, char *file, int line)
+{
+    LOG_ERROR_SCT("GENERAL", "Function %s in file %s at line %d not implemented", func, file, line);
+    abort();
+}
+
+
diff --git a/src/config.c b/src/config.c
index 0568353ecc6664f43f9abc37dd8271851d0a61e7..29b85b49cee97229cc2d4afb4b79102793c93855 100644
--- a/src/config.c
+++ b/src/config.c
@@ -1,5 +1,6 @@
 #define _POSIX_C_SOURCE 200809L
 
+#include <common/common.h>
 #include <lektor/config.h>
 #include <lektor/database.h>
 #include <ini/ini.h>
@@ -27,7 +28,7 @@ load_so(const char *const mod_path, const char *const mod_init, void *mod)
     void *handle = dlopen(mod_path, RTLD_NOW);
 
     if (NULL == handle) {
-        fprintf(stderr, " ! load_so: libdl error in dlopen: %s\n", dlerror());
+        LOG_ERROR("libdl error in dlopen on file '%s': %s", mod_path, dlerror());
         return 1;
     }
 
@@ -35,7 +36,7 @@ load_so(const char *const mod_path, const char *const mod_init, void *mod)
     *(void **) (&module_set_function) = dlsym(handle, mod_init);
 
     if ((error = dlerror()) != NULL) {
-        fprintf(stderr, " ! load_so: libdl error in dlsym: %s\n", error);
+        LOG_ERROR("libdl error in dlsym on file '%s': %s\n", mod_path, error);
         return 1;
     }
 
@@ -49,8 +50,7 @@ load_module_by_name(sqlite3 *db, const char *name, void *mod)
 
     if (!database_config_get_text(db, name, "path", mod_path, PATH_MAX) ||
         !database_config_get_text(db, name, "load_function", mod_load, INI_MAX_LINE)) {
-        fprintf(stderr, " ! load_module_by_name: Module named %s is incomplete or is not "
-                "defined in config file", name);
+        LOG_ERROR("Module named %s is incomplete or is not defined in config file", name);
         return 1;
     }
 
@@ -60,10 +60,10 @@ load_module_by_name(sqlite3 *db, const char *name, void *mod)
 inline static int
 validate_conf(sqlite3 *db)
 {
-#define CHK_OPTION(section, name)                                                               \
-    if (!database_config_exists(db, section, name)) {                                           \
-        fprintf(stderr, " ! validate_conf: Missing option " name " in section " section "\n");  \
-        return 1;                                                                               \
+#define CHK_OPTION(section, name)                                       \
+    if (!database_config_exists(db, section, name)) {                   \
+        LOG_ERROR("%s", "Missing option "name" in section "section);    \
+        return 1;                                                       \
     }
 
     CHK_OPTION("externals", "mkvpropedit");
@@ -95,16 +95,8 @@ handler(void *user, const char *section, const char *name, const char *value, in
 handler(void *user, const char *section, const char *name, const char *value)
 {
 #endif
-    if (!section || !name || !value) {
-        fprintf(stderr, " . handler: I can't complete the database with incomplete lines\n");
-        return 1;
-    }
-
-    if (!database_config_set(user, section, name, value)) {
-        fprintf(stderr, " . handler: Failed to update the database\n");
-        return 0;
-    }
-
+    RETURN_UNLESS(section && name && value, "I can't complete the database with incomplete lines", 1);
+    RETURN_UNLESS(database_config_set(user, section, name, value), "Failed to update the database", 0);
     return 1;
 }
 
@@ -123,7 +115,7 @@ config_detect_file(char *conf, size_t conf_len)
     /* Try the current working dir config file. */
     if (getcwd(conf, conf_len - 1) != NULL) {
         strncat(conf, "/lektor.ini", conf_len - 1);
-        fprintf(stderr, " . config_detect_file: trying %s\n", conf);
+        LOG_INFO("Trying %s", conf);
         if (!access(conf, R_OK | F_OK))
             goto found;
     }
@@ -138,37 +130,36 @@ config_detect_file(char *conf, size_t conf_len)
         goto no_config_directory;
     memcpy(conf, home, (strlen(home) + 1) * sizeof(char));
     strncat(conf, "/.config/lektor/lektor.ini", conf_len - 1);
-    fprintf(stderr, " . config_detect_file: trying %s\n", conf);
+    LOG_INFO("Trying %s", conf);
     if (!access(conf, R_OK | F_OK))
         goto found;
 
 no_config_directory:
     /* Try the '/opt/lektor' file. */
     memcpy(conf, "/opt/lektor/lektor.ini", sizeof("/opt/lektor/lektor.ini"));
-    fprintf(stderr, " . config_detect_file: trying %s\n", conf);
+    LOG_INFO("Trying %s", conf);
     if (!access(conf, R_OK | F_OK))
         goto found;
 
     /* Try the '/usr/local/etc/lektor.ini' file. */
     memcpy(conf, "/usr/local/etc/lektor.ini", sizeof("/usr/local/etc/lektor.ini"));
-    fprintf(stderr, " . config_detect_file: trying %s\n", conf);
+    LOG_INFO("Trying %s", conf);
     if (!access(conf, R_OK | F_OK))
         goto found;
 
     /* Try the '/etc/lektor.ini' file. */
     memcpy(conf, "/etc/lektor.ini", sizeof("/etc/lektor.ini"));
-    fprintf(stderr, " . config_detect_file: trying %s\n", conf);
+    LOG_INFO("Trying %s", conf);
     if (!access(conf, R_OK | F_OK))
         goto found;
 
     /* Error */
-    fprintf(stderr, " ! config_detect_file: an error occured with file %s: %s\n",
-            conf, strerror(errno));
+    LOG_ERROR("An error occured with file %s: %s", conf, strerror(errno));
     if (is_malloc)
         free(conf);
     return 1;
 found:
-    fprintf(stderr, " . config_detect_file: using file %s\n", conf);
+    LOG_INFO("Using file %s", conf);
     return 0;
 }
 
@@ -176,12 +167,12 @@ int
 config_new(sqlite3 *db, const char *conf)
 {
     if (ini_parse(conf, handler, db)) {
-        fprintf(stderr, " . config_new: Failed to parse file %s\n", conf);
+        LOG_ERROR("Failed to parse file %s", conf);
         return 1;
     }
 
     if (validate_conf(db)) {
-        fprintf(stderr, " . config_new: Configuration file %s is incomplete\n", conf);
+        LOG_ERROR("Configuration file %s is incomplete", conf);
         return 1;
     }
 
@@ -193,17 +184,8 @@ config_open(sqlite3 *db)
 {
     char conf_file[PATH_MAX];
     int ret = 1;
-
-    if (config_detect_file(conf_file, PATH_MAX)) {
-        fprintf(stderr, " ! config_open: error while searching for a config file\n");
-        goto error;
-    }
-
-    if (config_new(db, conf_file)) {
-        fprintf(stderr, " ! config_open: failed to read configuration file\n");
-        goto error;
-    }
-
+    GOTO_IF(config_detect_file(conf_file, PATH_MAX), "Error while searching for a config file", error);
+    GOTO_IF(config_new(db, conf_file), "Failed to read configuration file", error);
     ret = 0;
 error:
     return ret;
diff --git a/src/database/config.c b/src/database/config.c
index f1311d3c2cfeafd0e46f1a486939f50bbb3e0715..a2aa57d5c9d17c37d66585e918b325dd4ce83d70 100644
--- a/src/database/config.c
+++ b/src/database/config.c
@@ -26,8 +26,7 @@ database_config_set(sqlite3 *db, const char *section, const char *key, const cha
     code = sqlite3_step(stmt);
 
     if (code != SQLITE_OK && code != SQLITE_DONE) {
-        fprintf(stderr, " ! database_config_set: Failed to insert or replace: %s\n",
-                sqlite3_errmsg(db));
+        LOG_ERROR_SCT("DB", "Failed to insert or replace: %s", sqlite3_errmsg(db));
         goto error;
     }
 
@@ -51,12 +50,7 @@ database_config_get_text(sqlite3 *db, const char *section, const char *key, char
     SQLITE_PREPARE(db, stmt, SQL_STMT, error);
     SQLITE_BIND_TEXT(db, stmt, 1, section, error);
     SQLITE_BIND_TEXT(db, stmt, 2, key, error);
-
-    if (sqlite3_step(stmt) != SQLITE_ROW) {
-        fprintf(stderr, " ! database_config_get_text: Failed to insert or replace: %s\n",
-                sqlite3_errmsg(db));
-        goto error;
-    }
+    SQLITE_STEP_ROW(db, stmt, error);
 
     row = (char *) sqlite3_column_text(stmt, 0);
     strncpy(value, row, len);
@@ -80,12 +74,7 @@ database_config_exists(sqlite3 *db, const char *section, const char *key)
     SQLITE_PREPARE(db, stmt, SQL_STMT, error);
     SQLITE_BIND_TEXT(db, stmt, 1, section, error);
     SQLITE_BIND_TEXT(db, stmt, 2, key, error);
-
-    if (sqlite3_step(stmt) != SQLITE_ROW) {
-        fprintf(stderr, " ! database_config_exists: No rows: %s\n",
-                sqlite3_errmsg(db));
-        goto error;
-    }
+    SQLITE_STEP_ROW(db, stmt, error);
 
     ret = true;
 error:
@@ -107,11 +96,7 @@ database_config_get_int(sqlite3 *db, const char *section, const char *key, int *
     SQLITE_BIND_TEXT(db, stmt, 1, section, error);
     SQLITE_BIND_TEXT(db, stmt, 2, key, error);
 
-    if (sqlite3_step(stmt) != SQLITE_ROW) {
-        fprintf(stderr, " ! database_config_get_int: Failed to insert or replace: %s\n",
-                sqlite3_errmsg(db));
-        goto error;
-    }
+    SQLITE_STEP_ROW(db, stmt, error);
 
     *value = sqlite3_column_int(stmt, 1);
     ret = true;
diff --git a/src/database/find.c b/src/database/find.c
index f76f556986fc660150160c44e76e350a72b82f9b..56ac88822330b08ef006a2050704fc3f8bb47447 100644
--- a/src/database/find.c
+++ b/src/database/find.c
@@ -10,11 +10,7 @@
 bool
 database_search_database_init(sqlite3 *db, char *col_name, char *rgx, struct lkt_search *ret)
 {
-    if (ret == NULL) {
-        fprintf(stderr, " ! database_search_init: Exit because return pointer is NULL\n");
-        return false;
-    }
-
+    RETURN_UNLESS(ret, "Exit because return pointer is NULL", false);
     static const char *SQL_STMT_TEMPLATE =
         "WITH content AS ("
         " SELECT kara.id AS id, string AS any_col, LENGTH(CAST(kara.id AS TEXT)) AS len"
@@ -40,11 +36,7 @@ error:
 bool
 database_search_queue_init(sqlite3 *db, char *col_name, char *rgx, struct lkt_search *ret)
 {
-    if (ret == NULL) {
-        fprintf(stderr, " ! database_search_init: Exit because return pointer is NULL\n");
-        return false;
-    }
-
+    RETURN_UNLESS(ret, "Exit because return pointer is NULL", false);
     static const char *SQL_STMT_TEMPLATE =
         "WITH content AS ("
         " SELECT kara_id AS id, string AS any_col, LENGTH(CAST(kara_id AS TEXT)) AS len"
@@ -76,10 +68,7 @@ database_search_iter(struct lkt_search *item)
 
     code = sqlite3_step(item->stmt);
 
-    if (item == NULL) {
-        fprintf(stderr, " * database_search_iter: Exit function because the sqlite3_stmt* is NULL\n");
-        return false;
-    }
+    RETURN_UNLESS(item, "Exit because item is NULL, end iterations", false);
 
     if (code == SQLITE_DONE)
         goto error_or_done;
@@ -100,7 +89,7 @@ database_search_iter(struct lkt_search *item)
     }
 
 error:
-    fprintf(stderr, " ! database_search_iter: sqlite3_step failed: %s\n", sqlite3_errmsg(item->db));
+    LOG_ERROR_SCT("DB", "sqlite3_step failed: %s", sqlite3_errmsg(item->db));
 error_or_done:
     sqlite3_finalize(item->stmt);
     return false;
diff --git a/src/database/open.c b/src/database/open.c
index 424fe81006eeca8549e8523e0462982ee5ec3db5..e27425871c58f447f4d42e94274ad81830e57628 100644
--- a/src/database/open.c
+++ b/src/database/open.c
@@ -52,32 +52,15 @@ is_sql_str_invalid(const char *str)
 bool
 database_new(sqlite3 **db)
 {
-    if (SQLITE_OK != sqlite3_enable_shared_cache(1)) {
-        fprintf(stderr, " ! database_new: Failed to enable shared cache\n");
-        return false;
-    }
-
-    if (sqlite3_soft_heap_limit64(HEAP_LIMIT_SOFT) < 0) {
-        fprintf(stderr, " ! database_new: Failed to set soft heap limit to %u\n", HEAP_LIMIT_SOFT);
-        return false;
-    }
-
-    if (sqlite3_hard_heap_limit64(HEAP_LIMIT_HARD) < 0) {
-        fprintf(stderr, " ! database_new: Failed to set soft heap limit to %u\n", HEAP_LIMIT_HARD);
-        return false;
-    }
-
     /* SQLITE_OPEN_FULLMUTEX for serialized mode */
     static int flags = SQLITE_OPEN_READWRITE    |
                        SQLITE_OPEN_SHAREDCACHE  |
                        SQLITE_OPEN_NOFOLLOW     |
                        SQLITE_OPEN_NOMUTEX;
-
-    if (SQLITE_OK != sqlite3_open_v2(":memory:", db, flags, NULL)) {
-        fprintf(stderr, " ! database_open: Failed to open :memory: database\n");
-        return false;
-    }
-
+    RETURN_IF(SQLITE_OK != sqlite3_enable_shared_cache(1), "Failed to enable shared cache", false);
+    RETURN_IF(sqlite3_soft_heap_limit64(HEAP_LIMIT_SOFT) < 0, "Failed to set soft heap limit", false);
+    RETURN_IF(sqlite3_hard_heap_limit64(HEAP_LIMIT_HARD) < 0, "Failed to set soft heap limit", false);
+    RETURN_IF(SQLITE_OK != sqlite3_open_v2(":memory:", db, flags, NULL), "Failed to open :memory:", false);
     SQLITE_EXEC(*db, SQL_MEM_SCHEM, err_not_init);
     return true;
 err_not_init:
@@ -95,7 +78,7 @@ __attach(sqlite3 *db, const char *name, const char *path)
     snprintf(SQL_ATTACH, LKT_MAX_SQLITE_STATEMENT - 1, SQL_ATTACH_TEMPLATE, path, name);
     SQL_ATTACH[LKT_MAX_SQLITE_STATEMENT - 1] = 0;
     SQLITE_EXEC(db, SQL_ATTACH, err_no_attach);
-    fprintf(stderr, " * Attached database '%s' with path '%s'\n", name, path);
+    LOG_INFO_SCT("DB", "Attached database '%s' with path '%s'", name, path);
     ret = true;
 err_no_attach:
     return ret;
@@ -111,7 +94,7 @@ __detach(sqlite3 *db, const char *name)
     snprintf(SQL_DETACH, LKT_MAX_SQLITE_STATEMENT - 1, SQL_DETACH_TEMPLATE, name);
     SQL_DETACH[LKT_MAX_SQLITE_STATEMENT - 1] = 0;
     SQLITE_EXEC(db, SQL_DETACH, err_no_detach);
-    fprintf(stderr, " * Detached database '%s'\n", name);
+    LOG_INFO_SCT("DB", "Detached database '%s'", name);
     ret = true;
 err_no_detach:
     return ret;
@@ -137,66 +120,58 @@ bool
 database_open(sqlite3 *db, const char *dbpath)
 {
     if (is_sql_str_invalid(dbpath)) {
-        fprintf(stderr, " ! database_open: The database path %s is invalid\n", dbpath);
+        LOG_ERROR("The database path '%s' is invalid", dbpath);
         return false;
     }
-
     return __attach(db, PROTECTED_DATABASE, dbpath);
 }
 
 bool
 database_attach(sqlite3 *db, const char *name, const char *dbpath)
 {
-    if (!strcasecmp(PROTECTED_DATABASE, name)) {
-        fprintf(stderr, " ! database_attach: The database " PROTECTED_DATABASE
-                " is protected, can't attach a database with the same name\n");
-        return false;
-    }
+    RETURN_UNLESS(strcasecmp(PROTECTED_DATABASE, name), " The database "PROTECTED_DATABASE
+                  " is protected, can't attach a database with the same name", false);
 
     if (is_sql_str_invalid(name)) {
-        fprintf(stderr, " ! database_attach: The database name %s is invalid\n", name);
+        LOG_ERROR("The database name '%s' is invalid", name);
         return false;
     }
 
     if (__is_attached(db, name)) {
-        fprintf(stderr, " ! database_detach: Database named %s is already attached\n", name);
+        LOG_ERROR("The database '%s' is already attached", name);
         return false;
     }
 
     if (!__attach(db, name, dbpath)) {
-        fprintf(stderr, " ! database_attach: Failed to attach database named '%s' with path '%s'\n",
-                name, dbpath);
+        LOG_ERROR_SCT("DB", "Failed to attach database named '%s' with path '%s'", name, dbpath);
         return false;
     }
 
-    fprintf(stderr, " * Database %s attached\n", name);
+    LOG_INFO_SCT("DB", "Database '%s' attached", name);
     return true;
 }
 
 bool
 database_detach(sqlite3 *db, const char *name)
 {
-    if (!strcasecmp(PROTECTED_DATABASE, name)) {
-        fprintf(stderr, " ! database_detach: The database " PROTECTED_DATABASE
-                " is protected, can't detach it\n");
-        return false;
-    }
+    RETURN_UNLESS(strcasecmp(PROTECTED_DATABASE, name), " The database "PROTECTED_DATABASE
+                  " is protected, can't detach it", false);
 
     if (is_sql_str_invalid(name)) {
-        fprintf(stderr, " ! database_detach: The database name %s is invalid\n", name);
+        LOG_ERROR("The database name '%s' is invalid", name);
         return false;
     }
 
     if (!__is_attached(db, name)) {
-        fprintf(stderr, " ! database_detach: Database named %s is not attached\n", name);
+        LOG_ERROR("The database '%s' is not attached", name);
         return false;
     }
 
     if (!__detach(db, name)) {
-        fprintf(stderr, " ! database_detach: Failed to detach database named %s\n", name);
+        LOG_ERROR_SCT("DB", "Failed to detach database named %s", name);
         return false;
     }
 
-    fprintf(stderr, " * Database %s detached\n", name);
+    LOG_INFO_SCT("DB", "Database '%s' detached", name);
     return true;
 }
diff --git a/src/database/playlist.c b/src/database/playlist.c
index 6967aca86bc2cd3e01ad67bdb3c8fb4d9e8fca5b..20c0cb812bfc0f2ff011aa8d4a207551f9a507e2 100644
--- a/src/database/playlist.c
+++ b/src/database/playlist.c
@@ -1,5 +1,6 @@
 #define _POSIX_C_SOURCE 200809L
 
+#include <common/common.h>
 #include <lektor/database.h>
 #include <lektor/macro.h>
 #include <stdio.h>
@@ -20,7 +21,6 @@ database_plt_create(sqlite3 *db, const char *name)
     SQLITE_BIND_TEXT(db, stmt, 1, name, error);
     SQLITE_STEP_DONE(db, stmt, error);
     sta = true;
-    fprintf(stderr, " . database_plt_create: Created playlist %s\n", name);
 error:
     sqlite3_finalize(stmt);
     return sta;
@@ -102,16 +102,12 @@ database_plt_export(sqlite3 *db, const char *name)
     sqlite3_stmt *stmt;
 
     if (is_sql_str_invalid(name)) {
-        fprintf(stderr, " . database_plt_export: Name of playlist '%s' is invalid\n",
-                name);
+        LOG_ERROR("The playlist name '%s' is invalid", name);
         goto error;
     }
 
-    if (!strcasecmp(name, PROTECTED_DATABASE)) {
-        fprintf(stderr, " . database_plt_export: Name '" PROTECTED_DATABASE
-                "' is protected, you can't use it for a playlist name\n");
-        goto error;
-    }
+    GOTO_UNLESS(strcasecmp(name, PROTECTED_DATABASE), "Name '"PROTECTED_DATABASE
+                "' is protected, you can't use it for a playlist name", error);
 
     snprintf(SQL_STMT, LKT_MAX_SQLITE_STATEMENT - 1, SQL_SCHEM, name);
     SQL_STMT[LKT_MAX_SQLITE_STATEMENT - 1] = 0;
@@ -119,8 +115,7 @@ database_plt_export(sqlite3 *db, const char *name)
     code = sqlite3_step(stmt);
 
     if (code != SQLITE_DONE && code != SQLITE_OK) {
-        fprintf(stderr, " . database_plt_export: Failed to create schema: %s\n",
-                sqlite3_errmsg(db));
+        LOG_ERROR_SCT("DB", "Failed to create schema: %s", sqlite3_errmsg(db));
         goto error;
     }
 
@@ -140,16 +135,12 @@ database_plt_import(sqlite3 *db, const char *name)
     char SQL_STMT[LKT_MAX_SQLITE_STATEMENT], ret = false;
 
     if (is_sql_str_invalid(name)) {
-        fprintf(stderr, " . database_plt_import: Name of playlist '%s' is invalid\n",
-                name);
+        LOG_ERROR("The playlist name '%s' is invalid", name);
         goto error;
     }
 
-    if (!strcasecmp(name, PROTECTED_DATABASE)) {
-        fprintf(stderr, " . database_plt_import: Name '" PROTECTED_DATABASE
-                "' is protected, you can't use it for a playlist name\n");
-        goto error;
-    }
+    GOTO_UNLESS(strcasecmp(name, PROTECTED_DATABASE), "Name '"PROTECTED_DATABASE
+                "' is protected, you can't use it for a playlist name", error);
 
     snprintf(SQL_STMT, LKT_MAX_SQLITE_STATEMENT - 1, SQL_SCHEM, name, name, name);
     SQL_STMT[LKT_MAX_SQLITE_STATEMENT - 1] = 0;
diff --git a/src/database/queue.c b/src/database/queue.c
index b12f602edb3610c8328c941f81d913924bb16a9c..bd5a5dc2ee96ed393617e5663532c2488633ed20 100644
--- a/src/database/queue.c
+++ b/src/database/queue.c
@@ -87,10 +87,7 @@ error:
 static bool
 queue_add_with_col_like_str(sqlite3 *db, const char *col, const char *val, int priority)
 {
-    if (is_sql_str_invalid(col)) {
-        fprintf(stderr, " ! queue_add_with_col_like_str: Column name %s is invalid\n", col);
-        return false;
-    }
+    RETURN_IF(is_sql_str_invalid(col), "Column name is invalid", false);
 
     char SQL[LKT_MAX_SQLITE_STATEMENT];
     static const char *SQL_STMT =
@@ -117,10 +114,7 @@ error:
 static bool
 queue_insert_with_col_like_str(sqlite3 *db, const char *col, const char *val, int pos)
 {
-    if (is_sql_str_invalid(col)) {
-        fprintf(stderr, " ! queue_insert_with_col_like_str: Column name %s is invalid\n", col);
-        return false;
-    }
+    RETURN_IF(is_sql_str_invalid(col), "Column name is invalid", false);
 
     char SQL[LKT_MAX_SQLITE_STATEMENT];
     static const char *SQL_STMT =
@@ -165,8 +159,7 @@ bool
 database_queue_insert_query(sqlite3 *db, const char *query, int pos)
 {
     bool status = queue_insert_with_col_like_str(db, LKT_DATABASE_KARA_ALL, query, pos);
-    fprintf(stderr, " . %s: %s '%s'\n", __func__,
-            status ? "Successfull add query" : "Failed add query", query);
+    LOG_INFO("%s '%s' at %d", status ? "Successfull add query" : "Failed add query", query, pos);
     return status;
 }
 
@@ -197,8 +190,7 @@ bool
 database_queue_add_query(sqlite3 *db, const char *query, int priority)
 {
     bool status = queue_add_with_col_like_str(db, LKT_DATABASE_KARA_ALL, query, priority);
-    fprintf(stderr, " . %s: %s '%s'\n", __func__,
-            status ? "Successfull add query" : "Failed add query", query);
+    LOG_INFO("%s '%s' with prio %d", status ? "Successfull add query" : "Failed add query", query, priority);
     return status;
 }
 
@@ -206,8 +198,7 @@ bool
 database_queue_add_author(sqlite3 *db, const char *author, int priority)
 {
     bool status = queue_add_with_col_like_str(db, LKT_DATABASE_NAME_KAUTHOR, author, priority);
-    fprintf(stderr, " . %s: %s '%s'\n", __func__,
-            status ? "Successfull add author" : "Failed add author", author);
+    LOG_INFO("%s '%s'", status ? "Successfull add author" : "Failed add author", author);
     return status;
 }
 
@@ -215,8 +206,7 @@ bool
 database_queue_add_language(sqlite3 *db, const char *language, int priority)
 {
     bool status = queue_add_with_col_like_str(db, LKT_DATABASE_NAME_KLANG, language, priority);
-    fprintf(stderr, " . %s: %s '%s'\n", __func__,
-            status ? "Successfull add language" : "Failed add language", language);
+    LOG_INFO("%s '%s'", status ? "Successfull add language" : "Failed add language", language);
     return status;
 }
 
@@ -224,8 +214,7 @@ bool
 database_queue_add_category(sqlite3 *db, const char *cat, int priority)
 {
     bool status = queue_add_with_col_like_str(db, LKT_DATABASE_NAME_KCAT, cat, priority);
-    fprintf(stderr, " . %s: %s '%s'\n", __func__,
-            status ? "Successfull add category" : "Failed add category", cat);
+    LOG_INFO("%s '%s'", status ? "Successfull add category" : "Failed add category", cat);
     return status;
 }
 
@@ -233,8 +222,7 @@ bool
 database_queue_add_type(sqlite3 *db, const char *type, int priority)
 {
     bool status = queue_add_with_col_like_str(db, LKT_DATABASE_NAME_KTYPE, type, priority);
-    fprintf(stderr, " . %s: %s '%s'\n", __func__,
-            status ? "Successfull add type" : "Failed add type", type);
+    LOG_INFO("%s '%s'", status ? "Successfull add type" : "Failed add type", type);
     return status;
 }
 
@@ -306,19 +294,18 @@ database_queue_next(sqlite3 *db, char filepath[PATH_MAX])
         if (filepath != NULL)
             strncpy(filepath, (const char *) sqlite3_column_text(stmt, 0), PATH_MAX);
         else {
-            fprintf(stderr, " ! database_queue_next: Failed to get file, id was %d\n", id);
+            LOG_ERROR("Failed to get file, id was %d", id);
             goto error;
         }
     }
 
     else if (code == SQLITE_DONE) {
-        fprintf(stderr, " * database_queue_next: Failed to get next, no setting current to NULL\n");
+        LOG_ERROR("%s", "Failed to get next, don't set current to NULL");
         goto error;
     }
 
     else {
-        fprintf(stderr, "database_queue_next: Failed to fetch next kara: %s\n",
-                sqlite3_errmsg(db));
+        LOG_ERROR_SCT("DB", "Failed to fetch next kara: %s", sqlite3_errmsg(db));
         goto error;
     }
 
@@ -367,19 +354,18 @@ database_queue_prev(sqlite3 *db, char filepath[PATH_MAX])
         if (filepath != NULL)
             strncpy(filepath, (const char *) sqlite3_column_text(stmt, 0), PATH_MAX);
         else {
-            fprintf(stderr, " ! database_queue_prev: Failed to get file, position was %d\n", id);
+            LOG_ERROR("Failed to get file, position was %d", id);
             goto error;
         }
     }
 
     else if (code == SQLITE_DONE) {
-        fprintf(stderr, " * database_queue_prev: Failed to get previous, no setting current to NULL\n");
+        LOG_ERROR("%s", "Failed to get previous, don't set current to NULL");
         goto error;
     }
 
     else {
-        fprintf(stderr, " ! database_queue_prev: Failed to fetch prev kara: %s\n",
-                sqlite3_errmsg(db));
+        LOG_ERROR_SCT("DB", "Failed to fetch prev kara: %s", sqlite3_errmsg(db));
         goto error;
     }
 
@@ -439,7 +425,7 @@ database_queue_move(sqlite3 *db, int from, int to)
     code = sqlite3_step(stmt);
 
     if (code != SQLITE_ROW && code != SQLITE_DONE && code != SQLITE_OK) {
-        fprintf(stderr, " ! database_queue_move: move failed.\n");
+        LOG_ERROR_SCT("DB", "%s", "Move failed");
         goto error;
     }
 
@@ -465,8 +451,7 @@ database_queue_play(sqlite3 *db, int pos)
     code = sqlite3_step(stmt);
 
     if (code != SQLITE_OK && code != SQLITE_DONE) {
-        fprintf(stderr, " ! database_queue_play: Failed to update queue_state: %s\n",
-                sqlite3_errmsg(db));
+        LOG_ERROR_SCT("DB", "Failed to update queue_state: %s", sqlite3_errmsg(db));
         goto error;
     }
 
@@ -492,15 +477,12 @@ database_queue_set_current_index(sqlite3 *db, int idx)
     char SQL_GET[LKT_MAX_SQLITE_STATEMENT];
 
     if (idx <= 0) {
-        fprintf(stderr, " ! database_queue_set_current_index: an idx of %d is invalid, must be >= 0\n", idx);
-        return false;
-    }
-
-    if (snprintf(SQL_GET, LKT_MAX_SQLITE_STATEMENT - 1, SQL_GET_TEMPLATE, idx) < 0) {
-        fprintf(stderr, " ! database_queue_set_current_index: snprintf failed\n");
+        LOG_ERROR_SCT("DB", "An idx of %d is invalid, must be >= 0", idx);
         return false;
     }
 
+    RETURN_IF(snprintf(SQL_GET, LKT_MAX_SQLITE_STATEMENT - 1, SQL_GET_TEMPLATE, idx) < 0, "Snprintf failed",
+              false);
     SQL_GET[LKT_MAX_SQLITE_STATEMENT - 1] = 0;
     SQLITE_EXEC(db, SQL_GET, error);
     return true;
@@ -530,8 +512,7 @@ database_queue_get_current_file(sqlite3 *db, char filepath[PATH_MAX])
         strncpy(filepath, (const char *) sqlite3_column_text(stmt, 0), PATH_MAX);
 
     else {
-        fprintf(stderr, "database_queue_get_current_file: Failed to fetch prev kara: %s\n",
-                sqlite3_errmsg(db));
+        LOG_ERROR_SCT("DB", "Failed to fetch prev kara: %s", sqlite3_errmsg(db));
         goto error;
     }
 
diff --git a/src/database/stickers.c b/src/database/stickers.c
index b36b4c70fb9aa0456de8edb198db8a953fd74f31..9d172285c9d6c4cb235f734fbc456240665036db 100644
--- a/src/database/stickers.c
+++ b/src/database/stickers.c
@@ -20,12 +20,7 @@ database_sticker_create(sqlite3 *db, const char *name)
         "WHERE NOT EXISTS (SELECT 1 FROM 'stickers' WHERE name = ?);";
     sqlite3_stmt *stmt;
     int ret = false;
-
-    if (strlen(name) == 0) {
-        fprintf(stderr, " ! database_sticker_create: A sticker name must be at least one character long\n");
-        return ret;
-    }
-
+    RETURN_IF(strlen(name) == 0, "A sticker name must be at least one character long", ret);
     SQLITE_PREPARE(db, stmt, INSERT, error);
     SQLITE_BIND_TEXT(db, stmt, 1, name, error);
     SQLITE_BIND_TEXT(db, stmt, 2, name, error);
@@ -43,12 +38,7 @@ database_sticker_delete(sqlite3 *db, const char *name)
         "DELETE FROM 'stickers' WHERE name = ?;";
     sqlite3_stmt *stmt;
     int ret = false;
-
-    if (strlen(name) == 0) {
-        fprintf(stderr, " ! database_sticker_delete: A sticker name must be at least one character long\n");
-        return ret;
-    }
-
+    RETURN_IF(strlen(name) == 0, "A sticker name must be at least one character long", ret);
     SQLITE_PREPARE(db, stmt, INSERT, error);
     SQLITE_BIND_TEXT(db, stmt, 1, name, error);
     SQLITE_STEP_OK(db, stmt, error);
@@ -87,7 +77,7 @@ database_sticker_list(sqlite3 *db, const char *type, struct sticker_callback *ca
             "LEFT OUTER JOIN 'stickers' "
             "ON 'stickers'.id = 'stickers.song'.sticker;";
     else {
-        fprintf(stderr, " . database_sticker_list: type '%s' is invalid\n", type);
+        LOG_ERROR_SCT("DB", "Type '%s' is invalid", type);
         return false;
     }
 
@@ -144,7 +134,7 @@ database_sticker_set(sqlite3 *db, const char *type, const char *name, int uri, i
             "FROM 'stickers'"
             "WHERE 'stickers'.id = ?;\n";
     else {
-        fprintf(stderr, " . database_sticker_set: Type '%s' is invalid\n", type);
+        LOG_ERROR_SCT("DB", "Type '%s' is invalid", type);
         return false;
     }
 
@@ -181,7 +171,7 @@ database_sticker_get(sqlite3 *db, const char *type, const char *name, int uri, s
             " ON 'stickers'.id = 'stickers.plt'.sticker"
             " AND 'stickers'.name = ?;\n";
     else {
-        fprintf(stderr, " . database_sticker_get: Type '%s' is invalid\n", type);
+        LOG_ERROR_SCT("DB", "Type '%s' is invalid", type);
         return false;
     }
 
@@ -220,7 +210,7 @@ database_sticker_delete_specify(sqlite3 *db, const char *type, int uri, const ch
 
     /* Base query. */
     if (strcasecmp("plt", type) && strcasecmp("song", type)) {
-        fprintf(stderr, " . database_sticker_get: Type '%s' is invalid\n", type);
+        LOG_ERROR_SCT("DB", "Type '%s' is invalid", type);
         return false;
     }
 
diff --git a/src/database/update.c b/src/database/update.c
index ae1c9c6dcd9c7cdf0c8fa2bba70a02c2c92030b7..a3b64b2a1cbac804dfa26fccf0c65f929d962291 100644
--- a/src/database/update.c
+++ b/src/database/update.c
@@ -15,11 +15,7 @@
 static bool
 database_add_kara(sqlite3 *db, const char *filename)
 {
-    if (db == NULL || filename == NULL) {
-        fprintf(stderr, " ! database_add_kara: Invalid arguments\n");
-        return false;
-    }
-
+    RETURN_UNLESS(db || filename, "Invalid argument", false);
     static const char *SQL_STMT =
         "INSERT INTO "
         "kara (song_name, source_name, category, song_type, language, file_path, is_new, author_name, author_year, song_number)"
@@ -32,15 +28,10 @@ database_add_kara(sqlite3 *db, const char *filename)
     struct tm *the_local_time = localtime(&the_time);
     char year[10];
     code = snprintf(year, 10, "%d", the_local_time->tm_year + 1900);
-
-    if (code < 0 || code >= 10) {
-        fprintf(stderr, " ! database_add_kara: Failed to get the year of the current date: %s\n", sqlite3_errmsg(db));
-        return false;
-    }
+    RETURN_IF(code < 0 || code >= 10, "Failed to get the year of the current date", false);
 
     if (kara_metadata_read(&data, filename) != 0) {
-        fprintf(stderr, " ! database_add_kara: Failed to get matadata of the kara %s: %s\n", filename,
-                sqlite3_errmsg(db));
+        LOG_ERROR("Failed to get mdt for the kara '%s'", filename);
         return false;
     }
 
@@ -56,8 +47,7 @@ database_add_kara(sqlite3 *db, const char *filename)
         (sqlite3_bind_text(stmt, 8, data.author_name, -1, 0) != SQLITE_OK)  ||
         (sqlite3_bind_int(stmt,  9, data.song_number) != SQLITE_OK)
        ) {
-        fprintf(stderr, " ! database_add_kara: failed to bind argument to stmt statement for kara %s: %s\n",
-                filename, sqlite3_errmsg(db));
+        LOG_ERROR_SCT("DB", "Failed to bind for kara %s: %s", filename, sqlite3_errmsg(db));
         goto error;
     }
 
@@ -71,12 +61,7 @@ error:
 bool
 database_update_add(sqlite3 *db, const char *kara_path, struct kara_metadata *mdt, uint64_t id, bool avail)
 {
-    if (db == NULL || kara_path == NULL || mdt == NULL) {
-        fprintf(stderr, " ! database_add_kara: Invalid arguments\n");
-        errno = EINVAL;
-        return false;
-    }
-
+    RETURN_UNLESS(db && kara_path && mdt, "Invalid argument", false);
     static const char *SQL_STMT =
         "INSERT OR REPLACE INTO "
         "kara (song_name, source_name, category, song_type, language, file_path, is_new, author_name, author_year, song_number, id, available)"
@@ -88,12 +73,7 @@ database_update_add(sqlite3 *db, const char *kara_path, struct kara_metadata *md
     struct tm *the_local_time = localtime(&the_time);
     char year[10];
     code = snprintf(year, 10, "%d", the_local_time->tm_year + 1900);
-
-    if (code < 0 || code >= 10) {
-        fprintf(stderr, " ! database_update_add: Failed to get the year of the current date: %s\n",
-                sqlite3_errmsg(db));
-        goto error_no_sqlite;
-    }
+    GOTO_IF(code < 0 || code >= 10, "Failed to get the year of the current date", error_no_sqlite);
 
     /* From here we initialize the sqlite stmt. */
 
@@ -111,8 +91,7 @@ database_update_add(sqlite3 *db, const char *kara_path, struct kara_metadata *md
         (sqlite3_bind_int(stmt, 10, id) != SQLITE_OK)                       ||
         (sqlite3_bind_int(stmt, 11, avail) != SQLITE_OK)
        ) {
-        fprintf(stderr, " ! database_update_add: failed to bind argument to stmt statement for kara %s: %s\n",
-                kara_path, sqlite3_errmsg(db));
+        LOG_ERROR_SCT("DB", "Failed to bind argument for kara %s: %s", kara_path, sqlite3_errmsg(db));
         goto error;
     }
 
@@ -130,12 +109,10 @@ database_update(sqlite3 *db, const char *kara_dir)
     DIR *d;
     struct dirent *dir;
     char path[PATH_MAX];
-
     memset(path, 0, PATH_MAX * sizeof(char));
-    d = opendir(kara_dir);
 
-    if (!d) {
-        fprintf(stderr, " ! database_update: Failed to open directory '%s': %s\n", kara_dir, strerror(errno));
+    if (!(d = opendir(kara_dir))) {
+        LOG_ERROR("Failed to open directory '%s': %s", kara_dir, strerror(errno));
         return false;
     }
 
@@ -152,7 +129,7 @@ database_update(sqlite3 *db, const char *kara_dir)
             database_update(db, path);
     }
 
-    fprintf(stderr, " * database_update: Passed directory '%s'\n", kara_dir);
+    LOG_INFO("Passed directory '%s'", kara_dir);
     closedir(d);
     return true;
 }
diff --git a/src/database/user.c b/src/database/user.c
index 38704430d4c7f979ddbff6989446caa320d3612d..b6330b2d3319591a86e21683105afc25d6d88176 100644
--- a/src/database/user.c
+++ b/src/database/user.c
@@ -13,8 +13,7 @@ database_user_authentificate(sqlite3 *db, const char *password)
     SQLITE_PREPARE(db, stmt, SQL_STMT, error);
     SQLITE_BIND_TEXT(db, stmt, 1, password, error);
     SQLITE_STEP_ROW(db, stmt, error);
-    fprintf(stderr, " * User authentification was successfull for user %s\n",
-            sqlite3_column_text(stmt, 0));
+    LOG_INFO_SCT("DB", "User authentification for '%s'", sqlite3_column_text(stmt, 0));
     ret = true;
 error:
     sqlite3_finalize(stmt);
@@ -31,7 +30,7 @@ database_user_add(sqlite3 *db, const char *username, const char *password)
     SQLITE_BIND_TEXT(db, stmt, 1, username, error);
     SQLITE_BIND_TEXT(db, stmt, 2, password, error);
     SQLITE_STEP_OK(db, stmt, error);
-    fprintf(stderr, " * User %s added successfully\n", username);
+    LOG_INFO_SCT("DB", "User '%s' successfully added", username);
     ret = true;
 error:
     sqlite3_finalize(stmt);
diff --git a/src/main/lkt.c b/src/main/lkt.c
index bd98e94dbfa2a3af823709d40a61dda3cba790fd..e74a7c665679c924058371f3a7af4d81410a83b7 100644
--- a/src/main/lkt.c
+++ b/src/main/lkt.c
@@ -1,7 +1,6 @@
 #define _POSIX_C_SOURCE 200809L
 
-#include <lektor/macro.h>
-#include <lektor/defines.h>
+#include <common/common.h>
 #include <lektor/net.h>
 #include <lektor/cmd.h>
 #include <arpa/inet.h>
@@ -58,7 +57,7 @@ help(void)
 static noreturn inline void
 fail(const char *message)
 {
-    fprintf(stderr, "%s\n", message);
+    LOG_ERROR("%s", message);
     exit(EXIT_FAILURE);
 }
 
diff --git a/src/main/lktadm.c b/src/main/lktadm.c
index 41da3dd82a4fb8381d514a7f5ff375b4e91f64e2..5676ad2ec7caaeeb574df389304d6e132722a3cb 100644
--- a/src/main/lktadm.c
+++ b/src/main/lktadm.c
@@ -1,8 +1,8 @@
 #define _POSIX_C_SOURCE 200809L
 #define _DEFAULT_SOURCE
 
-#include <lektor/macro.h>
-#include <lektor/defines.h>
+#include <common/common.h>
+
 #include <lektor/cmd.h>
 #include <lektor/config.h>
 #include <lektor/mkv.h>
@@ -91,7 +91,7 @@ conf__(struct lkt_cmd_args *args)
 {
     (void) args;
     fprintf(stdout, "%s\n", lkt_default_config_file);
-    fprintf(stderr, "You may redirect this output to ~/.config/lektor/lektor.ini\n");
+    LOG_INFO("%s", "You may redirect this output to ~/.config/lektor/lektor.ini");
     exit(EXIT_SUCCESS);
 }
 
@@ -151,7 +151,7 @@ init_file_prompt__(struct lkt_cmd_args *args)
     int i, num;
     struct kara_metadata data;
     for (i = 0; i < args->argc; ++i) {
-        fprintf(stdout, "Asking for file '%s':\n", args->argv[i]);
+        LOG_INFO("Asking for file '%s':", args->argv[i]);
 
         if (get_stdin_line(" song_name (TITLE): ", data.song_name, LEKTOR_TAG_MAX))
             fail("Input error");
@@ -177,8 +177,8 @@ init_file_prompt__(struct lkt_cmd_args *args)
             fail("Input error");
 
         kara_metadata_write(&data, args->argv[i], mkvpropedit);
-        fprintf(stdout, "You may name this kara with the following name: '%s - %s%d - %s'\n",
-                data.source_name, data.song_type, data.song_number, data.song_name);
+        LOG_INFO("You may name this kara with the following name: '%s - %s%d - %s'",
+                 data.source_name, data.song_type, data.song_number, data.song_name);
     }
     exit(EXIT_SUCCESS);
 }
diff --git a/src/main/server.c b/src/main/server.c
index ba4eea5e717579761bc9d1d318948ee9afb2365d..434784e236c647b1bed0f592bff44b040160470e 100644
--- a/src/main/server.c
+++ b/src/main/server.c
@@ -1,14 +1,17 @@
 #define _POSIX_C_SOURCE 200809L
 
+#include <common/common.h>
 #include <lektor/config.h>
 #include <lektor/net.h>
 #include <lektor/database.h>
+#include <mthread/mthread.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
 #include <strings.h>
 #include <pwd.h>
+#include <pthread.h>
 
 /* Prints the help of the program */
 static void
@@ -35,10 +38,9 @@ print_help(void)
 int
 main(int argc, char *argv[])
 {
-    /* Variable initialisation */
     struct passwd *pw = getpwuid(getuid());
+    pthread_t th;
 
-    /* Argument handleing */
     if (argc <= 1)
         goto normal_launch;
     else if (strcasecmp(argv[1], "--help") == 0 ||
@@ -54,8 +56,8 @@ main(int argc, char *argv[])
     return EXIT_FAILURE;
 
 normal_launch:
-    fprintf(stderr, " * Lektor launched by user %s (shell: %s, home: %s)\n",
-            pw->pw_name, pw->pw_shell, pw->pw_dir);
-
+    LOG_INFO("Lektor launched by user %s (shell: %s, home: %s)", pw->pw_name, pw->pw_shell, pw->pw_dir);
+    mthread_init();
+    pthread_create(&th, NULL, mthread_main, NULL);
     return lkt_listen();
 }
diff --git a/src/mkv/write.c b/src/mkv/write.c
index 4ebd6b9cc5f84903e83db2c6acb2fab6c366a534..c5caa8c0d45cef00fcfa3e4dc6f709487b249051 100644
--- a/src/mkv/write.c
+++ b/src/mkv/write.c
@@ -1,8 +1,7 @@
 #define _POSIX_C_SOURCE 200809L
 #define _DEFAULT_SOURCE
 
-#include <lektor/macro.h>
-#include <lektor/defines.h>
+#include <common/common.h>
 #include <lektor/mkv.h>
 #include <stdbool.h>
 #include <stdio.h>
@@ -66,12 +65,12 @@ mkvpropedit__(const char *const args[])
 
     if ((pid = fork()) == 0) {
         if ((fd = open("/dev/null", O_WRONLY | O_TRUNC)) < 0) {
-            fprintf(stderr, " ! call_mkvpropedit: can't to open /dev/null in O_WRONLY O_TRUNC\n");
+            LOG_ERROR_SCT("FORK", "%s", "Can't to open /dev/null in O_WRONLY O_TRUNC");
             return false;
         }
 
         if (dup2(fd, 1) < 0) {
-            fprintf(stderr, " ! call_mkvpropedit: failed to duplicate /dev/null to stdout\n");
+            LOG_ERROR_SCT("FORK", "%s", "Failed to duplicate /dev/null to stdout");
             return false;
         }
 
@@ -80,21 +79,20 @@ mkvpropedit__(const char *const args[])
     }
 
     else if (pid < 0) {
-        fprintf(stderr, " ! metadata_write: failed to fork: %s\n", strerror(errno));
+        LOG_ERROR_SCT("FORK", "Failed to fork: %s\n", strerror(errno));
         return false;
     }
 
     else {
         do {
             if (waitpid(pid, &wstatus, WUNTRACED | WCONTINUED) == -1) {
-                fprintf(stderr, " ! metadata_write: Failed to wait children: %s\n",
-                        strerror(errno));
+                LOG_ERROR_SCT("FORK", "Failed to wait children: %s", strerror(errno));
                 return false;
             }
         } while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus));
 
         if ((status = WEXITSTATUS(wstatus))) {
-            fprintf(stderr, " ! metadata_write: children failed with status %d", status);
+            LOG_ERROR_SCT("FORK", "Children failed with status %d", status);
             return false;
         }
     }
@@ -116,15 +114,13 @@ kara_metadata_write(struct kara_metadata *mdt, const char *filename, const char
     strncat(tmpfilepath, "all:/tmp/lektor.metadata.XXXXXX", PATH_MAX - 1);
 
     if ((fd = mkstemp(metadafilepath)) < 0) {
-        fprintf(stderr, " ! metadata_write: failed to create temporary file: %s\n",
-                strerror(errno));
+        LOG_ERROR("Failed to create temporary file: %s", strerror(errno));
         goto error;
     }
 
     if (dprintf(fd, METADATA_TEMPLATE, mdt->source_name, mdt->song_name, mdt->category,
                 mdt->language, mdt->author_name, mdt->song_type, mdt->song_number) < 0) {
-        fprintf(stderr, " ! metadata_write: failed to write to temporary file: %s\n",
-                metadafilepath);
+        LOG_ERROR("Failed to write to temporary file: %s", metadafilepath);
         goto error;
     }
 
@@ -171,11 +167,11 @@ metadata_from_path(char *const mkvfile, struct kara_metadata *meta)
         memcpy(msgbuf, mkvfile + pmatch[7].rm_so, pmatch[7].rm_eo - pmatch[7].rm_so);
         memcpy(meta->song_name, mkvfile + pmatch[8].rm_so, pmatch[8].rm_eo - pmatch[8].rm_so);
     } else if (REG_NOMATCH == reti) {
-        fprintf(stderr, "No match for: %s\n", mkvfile);
+        LOG_ERROR("No match for: %s", mkvfile);
         goto error;
     } else {
         regerror(reti, &regex, msgbuf, sizeof(msgbuf));
-        fprintf(stderr, "Failed to execute regex: %s\n", msgbuf);
+        LOG_ERROR("Failed to execute regex: %s", msgbuf);
         goto error;
     }
 
@@ -185,7 +181,7 @@ metadata_from_path(char *const mkvfile, struct kara_metadata *meta)
 
     sta = true;
 error:
-    regfree(&regex);
+    /* regfree(&regex); // Should not be nescessary. */
     return sta;
 }
 
@@ -196,13 +192,10 @@ metadata_set_directory(const char *kara_dir, const char *mkvpropedit)
     struct dirent *dir;
     char path[PATH_MAX];
     struct kara_metadata meta;
-
     memset(path, 0, PATH_MAX * sizeof(char));
-    d = opendir(kara_dir);
 
-    if (!d) {
-        fprintf(stderr, " ! metadata_set_directory: Failed to open directory '%s': %s\n",
-                kara_dir, strerror(errno));
+    if (!(d = opendir(kara_dir))) {
+        LOG_ERROR("Failed to open directory '%s': %s", kara_dir, strerror(errno));
         return 1;
     }
 
@@ -221,7 +214,7 @@ metadata_set_directory(const char *kara_dir, const char *mkvpropedit)
             metadata_set_directory(path, mkvpropedit);
     }
 
-    fprintf(stderr, " * metadata_set_directory: Passed directory '%s'\n", kara_dir);
+    LOG_INFO("Passed directory '%s'", kara_dir);
     closedir(d);
     return false;
 }
diff --git a/src/module/module_sdl2.c b/src/module/module_sdl2.c
index 94c2e957e5120a4e9bc81129ee2d42ed961a9f07..6ec3defe8b96ce5667fc47e791bc5cc82ecb62b1 100644
--- a/src/module/module_sdl2.c
+++ b/src/module/module_sdl2.c
@@ -1,9 +1,9 @@
 #define _POSIX_C_SOURCE 200809L
 
+#include <common/common.h>
 #include <lektor/module/module_sdl2.h>
 #include <lektor/module/mpv.h>
-#include <lektor/macro.h>
-#include <lektor/defines.h>
+#include <mthread/mthread.h>
 #include <lektor/thread.h>
 
 #include <sched.h>
@@ -33,8 +33,8 @@ struct module_sdl2_window {
     volatile int mpv_duration;  /* Because don't need to be persistent  */
 
     /* Thread related */
-    pthread_mutex_t mtx;
-    struct lkt_thread self;
+    mthread_mutex_t mtx;
+    struct poller_thread self;
     volatile int launched;  /* SDL you sucks */
 };
 
@@ -84,7 +84,7 @@ init_mpv_gl__(mpv_handle *mpv, mpv_render_context **mpv_gl, mpv_render_param *pa
     wakeup_on_mpv_events = SDL_RegisterEvents(1);
     if (wakeup_on_mpv_render_update == (Uint32) - 1 ||
         wakeup_on_mpv_events == (Uint32) - 1) {
-        fprintf(stderr, " . init_mpv_gl__: Failed to register events\n");
+        LOG_ERROR_SCT("WINDOW", "%s", "Failed to register event");
         return 1;
     }
 
@@ -95,7 +95,7 @@ init_mpv_gl__(mpv_handle *mpv, mpv_render_context **mpv_gl, mpv_render_param *pa
 /* Thread related functions */
 
 static void *
-sdl_thread__(struct lkt_thread_arg *arg)
+sdl_thread__(struct poller_thread_arg *arg)
 {
     volatile struct lkt_win *const win       = arg->args;
     volatile struct module_sdl2_window *sdl2 = win->window;
@@ -118,8 +118,7 @@ sdl_thread__(struct lkt_thread_arg *arg)
 
     /* Init mpv here */
 
-    RETURN_IF(init_mpv__((mpv_handle **) & sdl2->mpv),
-              "Failed to init mpv", false);
+    RETURN_IF(init_mpv__((mpv_handle **) & sdl2->mpv), "Failed to init mpv", false);
 
     mpv_render_param params[] = {
         { MPV_RENDER_PARAM_API_TYPE, MPV_RENDER_API_TYPE_OPENGL },
@@ -145,11 +144,10 @@ sdl_thread__(struct lkt_thread_arg *arg)
     while (!sdl2->launched)
         sched_yield();
 
-    fprintf(stderr, " * Started SDL thread\n");
+    LOG_INFO_SCT("WINDOW", "%s", "Started SDL thread");
 
 loop:
-    if (SDL_PollEvent(&event))
-        sched_yield();
+    SDL_WaitEvent(&event);
 
     switch (event.type) {
     case SDL_QUIT:
@@ -208,6 +206,8 @@ loop:
         SDL_GL_SwapWindow((SDL_Window *) sdl2->window);
         redraw = 0;
     }
+
+    sched_yield();
     goto loop;  /* A loop without indentation. */
 }
 
@@ -244,11 +244,11 @@ module_sdl2_new(struct lkt_win *const win)
         memset(win->window, 0, sizeof(struct module_sdl2_window));
 
         /* Yeah, this is how it is done. */
-        pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
+        mthread_mutex_t mtx = MTHREAD_MUTEX_INITIALIZER;
         ((struct module_sdl2_window *) win->window)->mtx = mtx;
 
         /* Start the SDL thread */
-        struct lkt_thread_arg *arg = calloc(1, sizeof(struct lkt_thread_arg));
+        struct poller_thread_arg *arg = calloc(1, sizeof(struct poller_thread_arg));
         RETURN_UNLESS(arg, "Out of memory", false);
         arg->args = win;
         RETURN_IF(lkt_th_new(&((struct module_sdl2_window *) win->window)->self,
diff --git a/src/module/module_x11.c b/src/module/module_x11.c
index 36787fd3c6726f4e8e68f26ba0cfa64ebfe5b323..a8f821c53b4a4b1aff0ab9ed6b0e1a86e2473e5d 100644
--- a/src/module/module_x11.c
+++ b/src/module/module_x11.c
@@ -142,7 +142,7 @@ lx11_handle(struct module_x11_window *win)
 
         // Key press
         if (event.type == KeyRelease) {
-            fprintf(stderr, " . lx11_handle: released key with keycode: 0x%x\n", event.xkey.keycode);
+            LOG_INFO_SCT("WINDOW", "Release key: 0x%x", event.xkey.keycode);
             if (event.xkey.keycode == 0x47)
                 XClearWindow(win->master_display, win->master_win);
         }
@@ -178,7 +178,7 @@ module_x11_new(struct lkt_win *const win)
         memset(x11_win, 0, sizeof(struct module_x11_window));
         RETURN_UNLESS(x11_win, "Out of memory", false);
         RETURN_UNLESS(lx11_new(x11_win), "Can't create X11 window", false);
-        fprintf(stderr, " . module_x11_window: successfully created the X11 window\n");
+        LOG_INFO_SCT("WINDOW", "%s", "Created the X11 window");
     }
 
     if (x11_win->mpv == NULL) {
diff --git a/src/module/mpv.c b/src/module/mpv.c
index c5d5626c4baab06492aa918ea7a8cc975dad6408..bf193417d71e92510491f64c558b4ec03d95edfd 100644
--- a/src/module/mpv.c
+++ b/src/module/mpv.c
@@ -1,3 +1,6 @@
+#define _POSIX_C_SOURCE 200809L
+
+#include <common/common.h>
 #include <lektor/module/mpv.h>
 #include <lektor/commands.h>
 #include <lektor/database.h>
@@ -15,7 +18,7 @@ lmpv_free(mpv_handle **ctx)
     mpv_command(*ctx, cmd);
     mpv_destroy(*ctx);
     *ctx = NULL;
-    fprintf(stderr, " * lmpv_free: mpv context destroyed\n");
+    LOG_INFO_SCT("WINDOW", "%s", "The mpv context was destroyed");
 }
 
 mpv_handle *
@@ -27,8 +30,8 @@ lmpv_prepare(void)
 
 #define MPV_SET_OPTION(opt, value)                                  \
     if ((status = mpv_set_option_string(ctx, opt, value)) < 0) {    \
-        fprintf(stderr, " . %s: Failed to set %s to %s: %s\n",      \
-                __func__, opt, value, mpv_error_string(status));    \
+        LOG_ERROR_SCT("WINDOW", "Failed to set %s to %s: %s",       \
+                opt, value, mpv_error_string(status));              \
         return NULL;                                                \
     }
 
@@ -58,23 +61,21 @@ lmpv_new(unsigned long int wid)
     int status;
 
     if ((status = mpv_set_property(ctx, "wid", MPV_FORMAT_INT64, &wid)) < 0) {
-        fprintf(stderr, " ! lmpv_new: failed to set wid: %s\n",
-                mpv_error_string(status));
+        LOG_ERROR_SCT("WINDOW", "Failed to set wid: %s", mpv_error_string(status));
         goto error;
     }
 
     if ((status = mpv_initialize(ctx)) < 0) {
-        fprintf(stderr, " ! lmpv_new: failed to initialize mpv: %s\n",
-                mpv_error_string(status));
+        LOG_ERROR_SCT("WINDOW", "Failed to init mpv: %s", mpv_error_string(status));
         goto error;
     }
 
     if (!lmpv_observe_properties(ctx)) {
-        fprintf(stderr, " * lmpv_new: failed to observe properties\n");
+        LOG_ERROR_SCT("WINDOW", "%s", "Failed to observe properties");
         goto error;
     }
 
-    fprintf(stderr, " * lmpv_new: successfully created a mpv context\n");
+    LOG_ERROR_SCT("WINDOW", "%s", "Create mpv context");
     return ctx;
 error:
     lmpv_free(&ctx);
@@ -91,8 +92,7 @@ lmpv_set_volume(mpv_handle *ctx, int vol)
     snprintf(str, 4, "%d", vol);
     const char *cmd[] = {"set", "ao-volume", str, NULL};
     if ((status = mpv_command_async(ctx, 0, cmd)) < 0) {
-        fprintf(stderr, " ! lmpv_set_volume: Failed to execute command: %s\n",
-                mpv_error_string(status));
+        LOG_ERROR_SCT("WINDOW", "Failed to execute command: %s", mpv_error_string(status));
         return 1;
     }
     return 0;
@@ -106,8 +106,7 @@ lmpv_load_file(mpv_handle *ctx, const char *file)
     const char *cmd[] = {"loadfile", file, "replace", NULL};
     int status;
     if ((status = mpv_command_async(ctx, 0, cmd)) < 0) {
-        fprintf(stderr, " ! lmpv_load_file: Failed to add '%s': %s\n",
-                file, mpv_error_string(status));
+        LOG_ERROR_SCT("WINDOW", "Failed to add '%s': %s", file, mpv_error_string(status));
         return 1;
     }
     return 0;
@@ -120,8 +119,7 @@ lmpv_toggle_pause(mpv_handle *ctx)
     const char *cmd[] = {"cycle", "pause", "up", NULL};
     int status;
     if ((status = mpv_command_async(ctx, 0, cmd)) < 0) {
-        fprintf(stderr, "lmpv_toggle_pause: Failed issue command: %s\n",
-                mpv_error_string(status));
+        LOG_ERROR_SCT("WINDOW", "Failed issus command: %s", mpv_error_string(status));
         return 1;
     }
     return 0;
@@ -187,7 +185,7 @@ loop:
             database_queue_set_paused(db, *(bool *) prop->data);
         break;
     default:
-        fprintf(stderr, " ! Unhandled mpv event: %s\n", mpv_event_name(event->event_id));
+        LOG_WARN_SCT("WINDOW", "Unhandled mpv event '%s'", mpv_event_name(event->event_id));
         break;
     }
     goto loop; /* A loop without indentation. */
diff --git a/src/mthread/mthread.c b/src/mthread/mthread.c
new file mode 100644
index 0000000000000000000000000000000000000000..4944174f9b70ed5ce3dae073c32d02ec46122b75
--- /dev/null
+++ b/src/mthread/mthread.c
@@ -0,0 +1,375 @@
+#define _POSIX_C_SOURCE 200809L
+
+#include <mthread/mthread_internal.h>
+#include <sched.h>
+#include <string.h>
+#include <unistd.h>
+
+#define MTHREAD_LWP 1
+
+static mthread_virtual_processor_t virtual_processors[MTHREAD_MAX_VIRUTAL_PROCESSORS];
+static mthread_list_t joined_list;
+
+/* Is mthread initialized */
+static volatile int is_mthread_init = 0;
+
+static void
+__mthread_clear_keys(mthread_t *thread)
+{
+    RETURN_UNLESS(thread, "Invalid argument", NOTHING);
+    unsigned int i;
+    struct keys_list *keys = &(*thread)->keys;
+
+    for (i = 0; i < keys->first_avail; ++i)
+        mthread_key_delete(keys->list[i]);
+
+    keys->first_avail = 0;
+}
+
+static inline void
+mthread_list_init(mthread_list_t *list)
+{
+    mthread_list_t INIT = MTHREAD_LIST_INIT;
+    *list = INIT;
+}
+
+static inline void
+mthread_init_thread(struct mthread_s *thread)
+{
+    thread->next   = NULL;
+    thread->status = RUNNING;
+    thread->res    = NULL;
+}
+
+void
+mthread_insert_first(struct mthread_s *item, mthread_list_t *list)
+{
+    mthread_spinlock_lock(&(list->lock));
+    if (list->first == NULL) {
+        item->next  = NULL;
+        list->first = item;
+        list->last  = item;
+    } else {
+        item ->next = list->first;
+        list->first = item;
+    }
+    mthread_spinlock_unlock(&(list->lock));
+}
+
+void
+mthread_insert_last(struct mthread_s *item, mthread_list_t *list)
+{
+    mthread_spinlock_lock(&(list->lock));
+    if (list->first == NULL) {
+        item->next  = NULL;
+        list->first = item;
+        list->last  = item;
+    } else {
+        item->next = NULL;
+        list->last->next = item;
+        list->last = item;
+    }
+    mthread_spinlock_unlock(&(list->lock));
+}
+
+struct mthread_s *
+mthread_remove_first(mthread_list_t *list)
+{
+    struct mthread_s *res = NULL;
+    mthread_spinlock_lock(&(list->lock));
+    if (list->first != NULL) {
+        res = (struct mthread_s *)list->first;
+        list->first = res->next;
+        if (list->first == NULL)
+            list->last = NULL;
+    }
+    mthread_spinlock_unlock(&(list->lock));
+    return res;
+}
+
+extern int
+mthread_list_test(struct mthread_s *item, mthread_list_t *list)
+{
+    struct mthread_s *it = (struct mthread_s *) list->last;
+
+    while (it && it != item)
+        it = (struct mthread_s *) it->next;
+
+    return it == item;
+}
+
+static inline int
+mthread_mctx_set(struct mthread_s *mctx,
+                 void (*func) (void *), char *stack, size_t size,
+                 void *arg)
+{
+    /* fetch current context */
+    RETURN_IF(getcontext(&(mctx->uc)) != 0, "Error in getcontext", 1);
+
+    /* remove parent link */
+    mctx->uc.uc_link = NULL;
+
+    /* configure new stack */
+    mctx->uc.uc_stack.ss_sp    = stack;
+    mctx->uc.uc_stack.ss_size  = size;
+    mctx->uc.uc_stack.ss_flags = 0;
+    mctx->stack                = stack;
+
+    /* configure startup function (with one argument) */
+    makecontext(&(mctx->uc), (void (*)(void)) func, 1 + 1, arg);
+    return 0;
+}
+
+static inline int
+mthread_mctx_swap(struct mthread_s *cur_mctx, struct mthread_s *new_mctx)
+{
+    swapcontext(&(cur_mctx->uc), &(new_mctx->uc));
+    return 0;
+}
+
+void
+__mthread_yield(mthread_virtual_processor_t *vp)
+{
+    struct mthread_s *current = (struct mthread_s *)vp->current;
+    struct mthread_s *next    = mthread_remove_first(&(vp->ready_list));
+
+    if (vp->resched != NULL) {
+        mthread_insert_last((struct mthread_s *)vp->resched, &(vp->ready_list));
+        vp->resched = NULL;
+    }
+
+    if (current != vp->idle) {
+        if ((current->status != BLOCKED) && (current->status != ZOMBIE)) {
+            if (current->status == RUNNING)
+                vp->resched = current;
+
+            else
+                not_implemented();
+        }
+
+        if (next == NULL)
+            next = vp->idle;
+    }
+
+    /* always true at this point - except for idle thread */
+    if (next != NULL) {
+        if (vp->current != next) {
+            vp->current = next;
+            mthread_mctx_swap(current, next);
+        }
+    }
+
+    vp = mthread_get_vp();
+
+    if (vp->p != NULL) {
+        mthread_spinlock_unlock(vp->p);
+        vp->p = NULL;
+    }
+}
+
+static void
+mthread_idle_task(void *arg)
+{
+    mthread_virtual_processor_t *vp = arg;
+    long j;
+    int done = 0;
+
+    vp->state = 1;
+    while (done == 0) {
+        done = 1;
+        sched_yield();
+        for (j = 0; j < MTHREAD_LWP; j++) {
+            if (virtual_processors[j].state == 0)
+                done = 0;
+        }
+    }
+    LOG_INFO_SCT("SCHEDULER", "Virtual processor %d started", vp->rank);
+    while (1)
+        __mthread_yield(vp);
+    not_implemented();
+}
+
+mthread_virtual_processor_t *
+mthread_get_vp()
+{
+    return &(virtual_processors[0]);
+}
+
+int
+mthread_get_vp_rank()
+{
+    return mthread_get_vp()->rank;
+}
+
+static inline void
+mthread_init_vp(mthread_virtual_processor_t *vp, struct mthread_s *idle,
+                struct mthread_s *current, int rank)
+{
+    vp->current = current;
+    vp->idle    = idle;
+    mthread_list_init(&(vp->ready_list));
+    vp->rank    = rank;
+    vp->resched = NULL;
+    vp->p       = NULL;
+}
+
+void *
+mthread_main(void *arg)
+{
+    (void)arg;
+    mthread_virtual_processor_t *vp = mthread_get_vp();
+    while (1) {
+        sched_yield();
+        sleep(1);
+        __mthread_yield(vp);
+    }
+    return NULL;
+}
+
+static inline void
+mthread_init_lib(long i)
+{
+    struct mthread_s *current = NULL;
+    char *stack               = (char *) safe_malloc(MTHREAD_DEFAULT_STACK);
+    struct mthread_s *mctx    = (struct mthread_s *) safe_malloc(sizeof(struct mthread_s));
+    mthread_init_thread(mctx);
+
+    mthread_list_init(&(joined_list));
+
+    if (i == 0) {
+        current = (struct mthread_s *)safe_malloc(sizeof(struct mthread_s));
+        mthread_init_thread(current);
+        current->__start_routine = mthread_main;
+        current->stack = NULL;
+    }
+
+    mthread_init_vp(&(virtual_processors[i]), mctx, mctx, i);
+    mthread_mctx_set(mctx, mthread_idle_task, stack, MTHREAD_DEFAULT_STACK, &(virtual_processors[i]));
+    if (i != 0) {
+        virtual_processors[i].current = mctx;
+        setcontext(&(mctx->uc));
+    } else
+        virtual_processors[i].current = current;
+}
+
+static void
+mthread_start_thread(void *arg)
+{
+    struct mthread_s *mctx = (struct mthread_s *)arg;
+    LOG_INFO_SCT("THREAD INIT", "Thread %p started", arg);
+    mthread_virtual_processor_t *vp = mthread_get_vp();
+    __mthread_yield(vp);
+    mctx->res    = mctx->__start_routine(mctx->arg);
+    mctx->status = ZOMBIE;
+    LOG_INFO_SCT("THREAD END", "Thread %p ended (%d)", arg, vp->rank);
+    vp = mthread_get_vp();
+    __mthread_yield(vp);
+}
+
+/* Function for handling threads.  */
+
+static inline void
+__mthread_lib_init(long i)
+{
+    mthread_init_lib(i);
+    virtual_processors[0].state = 1;
+    LOG_INFO_SCT("THREAD", "%s", "library started");
+}
+
+void
+mthread_init(void)
+{
+    if (is_mthread_init == 0) {
+        __mthread_lib_init(0);
+        is_mthread_init = 1;
+    }
+}
+
+/* Create a thread with given attributes ATTR (or default attributes
+   if ATTR is NULL), and call function START_ROUTINE with given
+   arguments ARG.  */
+int
+mthread_create(mthread_t *__threadp, const mthread_attr_t *__attr,
+               void *(*__start_routine) (void *), void *__arg)
+{
+    if (is_mthread_init == 0) {
+        __mthread_lib_init(0);
+        is_mthread_init = 1;
+    }
+
+    mthread_virtual_processor_t *vp = mthread_get_vp();
+
+    if (__attr == NULL) {
+        struct mthread_s *mctx = mthread_remove_first(&(joined_list));
+        if (mctx == NULL) {
+            mctx = safe_malloc(sizeof(struct mthread_s));
+            memset(mctx, 0, sizeof(struct mthread_s));
+        }
+        char *stack = mctx->stack ? mctx->stack : safe_malloc(MTHREAD_DEFAULT_STACK);
+
+        mthread_init_thread(mctx);
+        LOG_INFO_SCT("THREAD INIT", "Create thread %p", (void *) mctx);
+        mctx->arg = __arg;
+        mctx->__start_routine = __start_routine;
+        mthread_mctx_set(mctx, mthread_start_thread, stack, MTHREAD_DEFAULT_STACK, mctx);
+        mthread_insert_last(mctx, &(vp->ready_list));
+        *__threadp = mctx;
+    } else
+        not_implemented();
+
+    return 0;
+}
+
+/* Obtain the identifier of the current thread.  */
+mthread_t
+mthread_self(void)
+{
+    return (mthread_t) mthread_get_vp()->current;
+}
+
+/* Compare two thread identifiers.  */
+int
+mthread_equal(mthread_t __thread1, mthread_t __thread2)
+{
+    return (__thread1 == __thread2);
+}
+
+/* Terminate calling thread.  */
+void
+mthread_exit(void *__retval)
+{
+    mthread_virtual_processor_t *vp = mthread_get_vp();
+    struct mthread_s *mctx          = (struct mthread_s *) vp->current;
+    mctx->res    = __retval;
+    mctx->status = ZOMBIE;
+    LOG_INFO_SCT("THREAD END", "Thread %p exited", (void *) mctx);
+    __mthread_yield(vp);
+}
+
+/* Make calling thread wait for termination of the thread TH.  The
+   exit status of the thread is stored in *THREAD_RETURN, if THREAD_RETURN
+   is not NULL.  */
+int
+mthread_join(mthread_t __th, void **__thread_return)
+{
+    LOG_INFO_SCT("THREAD END", "Join thread %p", (void *) __th);
+
+    while (__th->status != ZOMBIE)
+        mthread_yield();
+
+    if (__thread_return != NULL)
+        *__thread_return = (void *)__th->res;
+
+    __mthread_clear_keys(&__th);
+    LOG_INFO_SCT("THREAD END", "Thread %p joined", (void *) __th);
+    mthread_insert_last(__th, &(joined_list));
+    return 0;
+}
+
+void
+mthread_yield()
+{
+    mthread_virtual_processor_t *vp = mthread_get_vp();
+    LOG_INFO_SCT("THREAD YIELD", "Thread %p yield", (void *) vp->current);
+    __mthread_yield(vp);
+}
diff --git a/src/mthread/mthread_cond.c b/src/mthread/mthread_cond.c
new file mode 100644
index 0000000000000000000000000000000000000000..00ebe3098d947dd670f2c56511f250828b9bc2bf
--- /dev/null
+++ b/src/mthread/mthread_cond.c
@@ -0,0 +1,94 @@
+#include <mthread/mthread_internal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Functions for handling conditional variables.  */
+
+/* Initialize condition variable COND using attributes ATTR, or use
+   the default values if later is NULL.  */
+int
+mthread_cond_init(mthread_cond_t *__cond, const mthread_condattr_t *__cond_attr)
+{
+    (void)__cond_attr;
+    __cond->lock = 0;
+    __cond->list = safe_malloc(sizeof(struct mthread_list_s));
+    memset(__cond->list, 0, sizeof(struct mthread_list_s));
+    LOG_INFO("%s", "Successfully created a condition");
+    return 0;
+}
+
+/* Destroy condition variable COND.  */
+int
+mthread_cond_destroy(mthread_cond_t *__cond)
+{
+    if (__cond->list)
+        free((void *) __cond->list);
+    return 0;
+}
+
+/* Wake up one thread waiting for condition variable COND.  */
+int
+mthread_cond_signal(mthread_cond_t *__cond)
+{
+    mthread_t first, self = mthread_self();
+    mthread_virtual_processor_t *vp;
+    int ret = 1;
+    mthread_spinlock_lock(&__cond->lock);
+
+    if (__cond->list->first != NULL) {
+        first = mthread_remove_first(__cond->list);
+        vp = mthread_get_vp();
+        first->status = RUNNING;
+        mthread_insert_last(first, &(vp->ready_list));
+        LOG_INFO("Wake up the thread %p from the thread %p", (void *) first, (void *) self);
+        ret = 0;
+    } else
+        LOG_INFO("No thread to wake up found from the thread %p", (void *) self);
+
+    mthread_spinlock_unlock(&__cond->lock);
+    return ret;
+}
+
+/* Wake up all threads waiting for condition variables COND.  */
+int
+mthread_cond_broadcast(mthread_cond_t *__cond)
+{
+    mthread_t first, self = mthread_self();
+    mthread_virtual_processor_t *vp;
+    mthread_spinlock_lock(&__cond->lock);
+
+    while (__cond->list->first != NULL) {
+        first = mthread_remove_first(__cond->list);
+        vp = mthread_get_vp();
+        first->status = RUNNING;
+        LOG_INFO("Wake up the thread %p from the thread %p", (void *) first, (void *) self);
+        mthread_insert_last(first, &(vp->ready_list));
+    }
+
+    mthread_spinlock_unlock(&__cond->lock);
+    return 0;
+}
+
+/* Wait for condition variable COND to be signaled or broadcast.
+   MUTEX is assumed to be locked before.  */
+int
+mthread_cond_wait(mthread_cond_t *__cond, mthread_mutex_t *__mutex)
+{
+    mthread_spinlock_lock(&__cond->lock);
+    mthread_t self = mthread_self();
+    mthread_virtual_processor_t *vp = mthread_get_vp();
+    mthread_insert_last(self, __cond->list);
+    self->status = BLOCKED;
+    mthread_mutex_unlock(__mutex);
+
+    LOG_ERROR("Waiting in condition for thread %p", (void *) self);
+
+    vp->p = &__cond->lock;
+    mthread_yield();
+
+    mthread_mutex_lock(__mutex);
+    LOG_ERROR("Waike up in condition for thread %p", (void *) self);
+
+    return 0;
+}
diff --git a/src/mthread/mthread_debug.c b/src/mthread/mthread_debug.c
new file mode 100644
index 0000000000000000000000000000000000000000..8adcdc702748a074547bade6992c5654ceae9d2a
--- /dev/null
+++ b/src/mthread/mthread_debug.c
@@ -0,0 +1,11 @@
+#include <mthread/mthread_internal.h>
+#include <assert.h>
+
+void *
+safe_malloc(size_t size)
+{
+    void *tmp;
+    tmp = malloc(size);
+    assert(tmp != NULL);
+    return tmp;
+}
diff --git a/src/mthread/mthread_key.c b/src/mthread/mthread_key.c
new file mode 100644
index 0000000000000000000000000000000000000000..fc9741bc40ce4bff6fc115586a50de65e65ab9ce
--- /dev/null
+++ b/src/mthread/mthread_key.c
@@ -0,0 +1,172 @@
+#include <mthread/mthread_internal.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* TODO -> use binaty trees for better performance in searches. */
+typedef struct cell_s {
+    mthread_key_t key;
+    const volatile void *volatile spec;
+    void (* destr)(void *);
+    volatile struct cell_s *volatile next;
+} cell_t;
+
+/* Default is 0. Index start at zero. Because of that the vairables hold the
+   first available slots and not the last used slots in memory. */
+static volatile mthread_key_t first_avail_key   = 1;
+static volatile cell_t *volatile key_table      = NULL;
+
+/* Protects anything concerning the keys. */
+static mthread_tst_t lock = 0;
+
+/* Functions for handling thread-specific data. */
+
+/* Create a key value identifying a location in the thread-specific
+   data area.  Each thread maintains a distinct thread-specific data
+   area. DESTR_FUNCTION, if non-NULL, is called with the value
+   associated to that key when the key is destroyed.
+   DESTR_FUNCTION is not called if the value associated is NULL when
+   the key is destroyed. */
+int
+mthread_key_create(mthread_key_t *__key, void (*__destr_function) (void *))
+{
+    cell_t *new_cell = (cell_t *) safe_malloc(sizeof(cell_t));
+    mthread_spinlock_lock(&lock);
+
+    /* Creation. */
+    new_cell->key   = first_avail_key;
+    new_cell->spec  = NULL;
+    new_cell->destr = __destr_function;
+    new_cell->next  = NULL;
+
+    first_avail_key++;
+
+    if (key_table) {
+        LOG_INFO("Enqueue cell %d into global key list, old head is %d",
+                 new_cell->key, key_table->key);
+        new_cell->next = key_table;
+        key_table = new_cell;
+        LOG_INFO("New head is %d, next is %d",
+                 key_table->key, key_table->next->key);
+    } else {
+        LOG_INFO("%s", "Set base cell for global key list");
+        key_table = new_cell;
+    }
+
+    /* Register key into the thread. */
+
+    struct keys_list *keys = &(mthread_self()->keys);
+    if (!keys->list) {
+        keys->list = (mthread_key_t *) safe_malloc(INIT_KEYS_LIST * sizeof(mthread_key_t));
+        keys->first_avail = 0;
+        keys->size = INIT_KEYS_LIST;
+        LOG_INFO("%s", "Create the key list for thread");
+    }
+
+    if (keys->size == keys->first_avail) {
+        /* Extend size if no space left. */
+        void *new = realloc((void *) keys->list, (keys->size + INIT_KEYS_LIST) * sizeof(mthread_key_t));
+        GOTO_UNLESS(new, "Could not increase the size of the key list", end);
+        keys->size += INIT_KEYS_LIST;
+        keys->list = new;
+        LOG_INFO("%s", "Increase the size of the key list");
+    }
+
+    /* Register the key into the thread. */
+    keys->list[keys->first_avail] = new_cell->key;
+    keys->first_avail++;
+
+    /* Returns. */
+    *__key = new_cell->key;
+    LOG_INFO("Created key %d", *__key);
+end:
+    mthread_spinlock_unlock(&lock);
+    return errno;
+}
+
+/* Destroy KEY. */
+int
+mthread_key_delete(mthread_key_t __key)
+{
+    mthread_spinlock_lock(&lock);
+
+    cell_t *it = (cell_t *) key_table;
+    cell_t *last = NULL;
+
+    while (it != NULL && it->key != __key) {
+        last = it;
+        it = (cell_t *) it->next;
+    }
+
+    if (!it) {
+        errno = EINVAL;
+        LOG_ERROR("Key %u not found", __key);
+        goto end;
+    }
+
+    if (it->destr) {
+        LOG_INFO("Call function on data at %p", (void *) it->spec);
+        it->destr((void *) it->spec);
+    }
+
+    /* Handle deletion in the linked list. */
+
+    if (last)
+        last->next = it->next;
+    else
+        key_table = it->next;
+
+    free(it);
+    LOG_INFO("Deleted key %u successfully", __key);
+    /* End of the function, simply returns. */
+end:
+    mthread_spinlock_unlock(&lock);
+    return errno;
+}
+
+/* Store POINTER in the thread-specific data slot identified by KEY. */
+int
+mthread_setspecific(mthread_key_t __key, const void *__pointer)
+{
+    mthread_spinlock_lock(&lock);
+
+    cell_t *it = (cell_t *) key_table;
+    while (it != NULL && it->key != __key)
+        it = (cell_t *) it->next;
+
+    if (!it) {
+        errno = EINVAL;
+        LOG_ERROR("Key %u not found", __key);
+        goto end;
+    }
+
+    it->spec = __pointer;
+    LOG_INFO("Key %u set to %p", __key, __pointer);
+    /* End of the function, simply returns. */
+end:
+    mthread_spinlock_unlock(&lock);
+    return errno;
+}
+
+/* Return current value of the thread-specific data slot identified by KEY. */
+void *
+mthread_getspecific(mthread_key_t __key)
+{
+    void *ret = NULL;
+    mthread_spinlock_lock(&lock);
+
+    cell_t *it = (cell_t *) key_table;
+    while (it != NULL && it->key != __key)
+        it = (cell_t *) it->next;
+
+    if (!it) {
+        errno = EINVAL;
+        LOG_ERROR("Key %u not found", __key);
+        goto end;
+    }
+
+    ret = (void *) it->spec;
+    /* End of the function, simply returns. */
+end:
+    mthread_spinlock_unlock(&lock);
+    return (void *) ret;
+}
diff --git a/src/mthread/mthread_mutex.c b/src/mthread/mthread_mutex.c
new file mode 100644
index 0000000000000000000000000000000000000000..b82ec5f1924b9dabcc8996bd3a3185e72aa15ac1
--- /dev/null
+++ b/src/mthread/mthread_mutex.c
@@ -0,0 +1,119 @@
+#include <errno.h>
+#include <string.h>
+#include <mthread/mthread_internal.h>
+
+/* Functions for mutex handling.  */
+
+/* Initialize MUTEX using attributes in *MUTEX_ATTR, or use the
+   default values if later is NULL.  */
+int
+mthread_mutex_init(mthread_mutex_t *__mutex, const mthread_mutexattr_t *__mutex_attr)
+{
+    (void) __mutex_attr;
+
+    __mutex->list        = safe_malloc(sizeof(mthread_list_t));
+    __mutex->list->first = NULL;
+    __mutex->list->last  = NULL;
+    __mutex->nb_thread   = 0;
+    __mutex->lock        = 0;
+
+    LOG_INFO("%s", "MUTEX initialized");
+    return 0;
+}
+
+/* Destroy MUTEX.  */
+int
+mthread_mutex_destroy(mthread_mutex_t *__mutex)
+{
+    mthread_spinlock_lock(&__mutex->lock);
+
+    if (__mutex->nb_thread != 0)
+        return EBUSY;
+    free(__mutex->list);
+    __mutex->list = NULL;
+
+    mthread_spinlock_unlock(&__mutex->lock);
+    return 0;
+}
+
+/* Try to lock MUTEX.  */
+int
+mthread_mutex_trylock(mthread_mutex_t *__mutex)
+{
+    int retval = EINVAL;
+
+    if (__mutex->list == NULL) {
+        __mutex->list = safe_malloc(sizeof(mthread_list_t));
+        memset(__mutex->list, 0, sizeof(mthread_list_t));
+    }
+
+    mthread_spinlock_lock(&__mutex->lock);
+
+    if (__mutex->nb_thread == 0) {
+        __mutex->nb_thread = 1;
+        mthread_spinlock_unlock(&__mutex->lock);
+        retval = 0;
+        goto end;
+    }
+
+    mthread_spinlock_unlock(&__mutex->lock);
+    LOG_WARN("%s", "MUTEX busy");
+    retval = EBUSY;
+
+end:
+    return retval;
+}
+
+/* Wait until lock for MUTEX becomes available and lock it.  */
+int
+mthread_mutex_lock(mthread_mutex_t *__mutex)
+{
+    mthread_t self;
+    mthread_virtual_processor_t *vp;
+
+    if (__mutex->list == NULL) {
+        __mutex->list = safe_malloc(sizeof(mthread_list_t));
+        memset(__mutex->list, 0, sizeof(mthread_list_t));
+    }
+
+    mthread_spinlock_lock(&__mutex->lock);
+
+    if (__mutex->nb_thread == 0) {
+        __mutex->nb_thread = 1;
+        mthread_spinlock_unlock(&__mutex->lock);
+    } else {
+        self = mthread_self();
+        mthread_insert_last(self, __mutex->list);
+        self->status = BLOCKED;
+        vp = mthread_get_vp();
+        vp->p = &__mutex->lock;
+        mthread_yield();
+    }
+
+    return 0;
+}
+
+/* Unlock MUTEX.  */
+int
+mthread_mutex_unlock(mthread_mutex_t *__mutex)
+{
+    mthread_t first;
+    mthread_virtual_processor_t *vp;
+
+    if (__mutex->list == NULL) {
+        __mutex->list = safe_malloc(sizeof(mthread_list_t));
+        memset(__mutex->list, 0, sizeof(mthread_list_t));
+    }
+
+    mthread_spinlock_lock(&__mutex->lock);
+    if (__mutex->list->first != NULL) {
+        first = mthread_remove_first(__mutex->list);
+        vp = mthread_get_vp();
+        first->status = RUNNING;
+        mthread_insert_last(first, &(vp->ready_list));
+    } else
+        __mutex->nb_thread = 0;
+
+    mthread_spinlock_unlock(&__mutex->lock);
+    return 0;
+}
diff --git a/src/mthread/mthread_once.c b/src/mthread/mthread_once.c
new file mode 100644
index 0000000000000000000000000000000000000000..acab4bcf75aae21aa4fb6314e77fd3e23ef7f3ab
--- /dev/null
+++ b/src/mthread/mthread_once.c
@@ -0,0 +1,19 @@
+#include <mthread/mthread_internal.h>
+#include <errno.h>
+/* Functions for handling initialization.  */
+
+/* Guarantee that the initialization function INIT_ROUTINE will be called
+   only once, even if mthread_once is executed several times with the
+   same ONCE_CONTROL argument. ONCE_CONTROL must point to a static or
+   extern variable initialized to MTHREAD_ONCE_INIT.
+
+   The initialization functions might throw exception which is why
+   this function is not marked with .  */
+int
+mthread_once (mthread_once_t *__once_control, void (*__init_routine) (void))
+{
+    if (mthread_test_and_set(__once_control))
+        return EAGAIN;
+    __init_routine();
+    return 0;
+}
diff --git a/src/mthread/mthread_sem.c b/src/mthread/mthread_sem.c
new file mode 100644
index 0000000000000000000000000000000000000000..9c5534aea1896b3412f931fae2f0793b85fad2fb
--- /dev/null
+++ b/src/mthread/mthread_sem.c
@@ -0,0 +1,72 @@
+#include <mthread/mthread_internal.h>
+
+/* Functions for handling semaphore.  */
+
+int
+mthread_sem_init(mthread_sem_t *sem, unsigned int value)
+{
+    sem->value = value;
+    mthread_mutex_init(&sem->mutex, NULL);
+    return 0;
+}
+
+/* P(sem), wait(sem) */
+int
+mthread_sem_wait(mthread_sem_t *sem)
+{
+    /* Here we loop while we can't enter the semaphore because there are already
+       the maximum number of clients. */
+loop:
+    mthread_mutex_lock(&sem->mutex);
+    if (sem->value == 0) {
+        mthread_mutex_unlock(&sem->mutex);
+        mthread_yield();
+        goto loop;
+    }
+
+    sem->value --;
+    mthread_mutex_unlock(&sem->mutex);
+    return 0;
+}
+
+/* V(sem), signal(sem) */
+int
+mthread_sem_post(mthread_sem_t *sem)
+{
+    mthread_mutex_lock(&sem->mutex);
+    sem->value ++;
+    mthread_mutex_unlock(&sem->mutex);
+    return 0;
+}
+
+int
+mthread_sem_getvalue(mthread_sem_t *sem, int *sval)
+{
+    *sval = sem->value;
+    return 0;
+}
+
+int
+mthread_sem_trywait(mthread_sem_t *sem)
+{
+    if (mthread_mutex_trylock(&sem->mutex))
+        return 1;
+
+    if (sem->value == 0) {
+        mthread_mutex_unlock(&sem->mutex);
+        return 1;
+    }
+
+    sem->value --;
+    mthread_mutex_unlock(&sem->mutex);
+    return 0;
+}
+
+/* undo sem_init() */
+int
+mthread_sem_destroy(mthread_sem_t *sem)
+{
+    mthread_mutex_destroy(&sem->mutex);
+    sem->value = 0;
+    return 0;
+}
diff --git a/src/mthread/mthread_tst.c b/src/mthread/mthread_tst.c
new file mode 100644
index 0000000000000000000000000000000000000000..35bae1659166ef8c934ebbf7e2671a8bbe1623cf
--- /dev/null
+++ b/src/mthread/mthread_tst.c
@@ -0,0 +1,73 @@
+#include <mthread/mthread_internal.h>
+#include <sched.h>
+
+#if defined(i686_ARCH) || defined(x86_64_ARCH)
+
+static inline int
+__mthread_test_and_set(mthread_tst_t *atomic)
+{
+    int ret;
+    __asm__ __volatile__("lock; xchgl %0, %1":"=r"(ret), "=m"(*atomic):"0"(1), "m"(*atomic):"memory");
+    return ret;
+}
+
+#elif defined(sparc_ARCH)
+static inline int
+__mthread_test_and_set(mthread_tst_t *spinlock)
+{
+    char ret = 0;
+    __asm__ __volatile__("ldstub [%0], %1": "=r"(spinlock), "=r"(ret): "0"(spinlock), "1" (ret) : "memory");
+    return (unsigned) ret;
+}
+
+#elif defined(ia64_ARCH)
+static __inline__ int
+__mthread_test_and_set(mthread_tst_t *atomic)
+{
+    int ret;
+    __asm__ __volatile__("xchg4 %0=%1, %2":"=r"(ret), "=m"(*atomic):"0"(1), "m"(*atomic):"memory");
+    return ret;
+}
+#else
+#define USE_GENERIC_ASM
+#include <pthread.h>
+static pthread_mutex_t tst_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static inline int
+__mthread_test_and_set(mthread_tst_t *atomic)
+{
+    int res;
+    pthread_mutex_lock(&tst_mutex);
+    res = *atomic;
+    if (*atomic == 0)
+        *atomic = 1;
+    pthread_mutex_unlock(&tst_mutex);
+    return res;
+}
+#endif
+
+int
+mthread_test_and_set(mthread_tst_t *atomic)
+{
+    return __mthread_test_and_set(atomic);
+}
+
+void
+mthread_spinlock_lock(mthread_tst_t *atomic)
+{
+#ifdef USE_GENERIC_ASM
+    static pthread_mutex_t spin_tst_mutex = PTHREAD_MUTEX_INITIALIZER;
+    pthread_mutex_lock(&spin_tst_mutex);
+#endif
+    while (mthread_test_and_set(atomic))
+        sched_yield();
+#ifdef USE_GENERIC_ASM
+    pthread_mutex_unlock(&spin_tst_mutex);
+#endif
+}
+
+void
+mthread_spinlock_unlock(mthread_tst_t *atomic)
+{
+    *atomic = 0;
+}
diff --git a/src/net/command.c b/src/net/command.c
index 33df59b7ea8856018480657dc5c2f7e42ab93932..b23744f20595f3ac815365ffbd850c756f666330 100644
--- a/src/net/command.c
+++ b/src/net/command.c
@@ -1,5 +1,6 @@
 #define _POSIX_C_SOURCE 200809L
 
+#include <common/common.h>
 #include <lektor/net.h>
 
 #include <stdlib.h>
diff --git a/src/net/listen.c b/src/net/listen.c
index ef241e29f1d82c32e3c32813154b941dcdfdf5b6..c27977ada5d926b476d3d8685adc4a4dc9aed694 100644
--- a/src/net/listen.c
+++ b/src/net/listen.c
@@ -2,7 +2,7 @@
 
 #include <lektor/commands.h>
 #include <lektor/macro.h>
-#include <lektor/defines.h>
+#include <lektor/define.h>
 #include <lektor/database.h>
 #include <lektor/repo.h>
 #include <lektor/net.h>
@@ -10,6 +10,7 @@
 
 #include <sqlite3.h>
 
+#include <sched.h>
 #include <assert.h>
 #include <arpa/inet.h>
 #include <errno.h>
@@ -119,13 +120,13 @@ static void
 send_status(struct lkt_state *srv, size_t c, int status, const char *cmd_name)
 {
     if (!status) {
-        printf(" * Command: %s\n", cmd_name);
+        LOG_INFO_SCT("COMMAND", " Command: %s", cmd_name);
         send_ok(srv, c);
     } else {
         if (status == 2)
-            printf(" * Unknown command: %s\n", cmd_name);
+            LOG_INFO_SCT("COMMAND", " Unknown command: %s", cmd_name);
         else
-            printf(" * Command failed: %s\n", cmd_name);
+            LOG_INFO_SCT("COMMAND", " Command failed: %s", cmd_name);
         send_ack(srv, c, cmd_name);
     }
 }
@@ -281,7 +282,7 @@ handle_simple_command(struct lkt_state *srv, size_t c, struct lkt_command cmd)
 
     continuation = lkt_get_continuation(srv, c);
     if (continuation > 0) {
-        fprintf(stderr, " * Client should continue from %d\n", continuation);
+        LOG_INFO_SCT("NETWORK", "Client should continue from %d", continuation);
         send_continue(srv, c, continuation);
     }
     send_status(srv, c, err, cmd.name);
@@ -329,7 +330,7 @@ handle_command(struct lkt_state *srv, size_t i, struct lkt_command cmd)
     else
         err = handle_simple_command(srv, i, cmd);
 
-    printf(" * Command result: %s -> %d\n", cmd.name, err);
+    LOG_INFO_SCT("COMMAND", "Command result: %s -> %d", cmd.name, err);
     return err;
 }
 
@@ -467,21 +468,21 @@ failure:
 
     if (fd < 0) {
         if (host)
-            fprintf(stderr, "Failed to bind to %s:%s\n", host, port);
+            LOG_ERROR_SCT("NETWORK", "Failed to bind to %s:%s", host, port);
         else
-            fprintf(stderr, "Failed to bind to port %s\n", port);
+            LOG_ERROR_SCT("NETWORK", "Failed to bind to port %s", port);
         return -1;
     }
 
     if (listen(fd, LKT_BACKLOG) < 0) {
-        perror("Failed to listen");
+        LOG_ERROR_SCT("NETWORK", "Failed to listen: %s", strerror(errno));
         return -1;
     }
 
     if (host)
-        printf(" * Listening on %s:%s\n", host, port);
+        LOG_INFO_SCT("NETWORK", "Listening on %s:%s", host, port);
     else
-        printf(" * Listening on port %s\n", port);
+        LOG_INFO_SCT("NETWORK", "Listening on port %s", port);
 
     return fd;
 }
@@ -506,7 +507,7 @@ accept_all(int listen_fd, struct pollfd *fds, size_t fds_max, size_t *fds_len)
         }
 
         if (*fds_len == fds_max) {
-            fprintf(stderr, "Maximum number of file descriptors reached.\n");
+            LOG_ERROR_SCT("NETWORK", "%s", "Maximum number of file descriptors reached");
             break;
         }
 
@@ -661,8 +662,7 @@ handle_idle_events(struct lkt_state *srv)
             strncat(msg->data, "message ", LKT_MESSAGE_MAX - 1);
 
         msg->data_len = strlen(msg->data);
-        fprintf(stderr, " * handle_idle_events: sending '%s' (len: %ld) to the client number %ld\n", msg->data,
-                msg->data_len, i);
+        LOG_INFO_SCT("COMMAND", "Sending '%s' (len: %ld) to client %ld", msg->data, msg->data_len, i);
         msg->data[msg->data_len - 1] = '\n';
         msg->data[msg->data_len] = '\0';
         lkt_state_send(srv, i, msg);
@@ -699,26 +699,22 @@ handle_repo_hevents(struct lkt_state *srv)
         /* Add the downloaded kara to the database. */
         case kara_action_add:
             if (!database_update_add(srv->db, kara->filename, &kara->mdt, kara->id, true)) {
-                fprintf(stderr, " ! handle_repo_hevents: Failed to add downloaded kara with id %lu and path %s\n",
-                        kara->id, kara->filename);
+                LOG_ERROR("Failed to add downloaded kara with id %lu and path %s", kara->id, kara->filename);
                 goto get_out;
             }
 
-            fprintf(stderr, " * Added kara %lu with path %s to database\n",
-                    kara->id, kara->filename);
+            LOG_INFO("Added kara %lu with path %s to database", kara->id, kara->filename);
 
             break;
 
         /* Add the mdt of the kara to the database. Mark it unavailable. */
         case kara_action_unavail:
             if (!database_update_add(srv->db, kara->filename, &kara->mdt, kara->id, false)) {
-                fprintf(stderr, " ! handle_repo_hevents: Failed to add kara with id %lu with flag unavailable\n",
-                        kara->id);
+                LOG_ERROR("Failed to add kara with id %lu with flag unavailable", kara->id);
                 goto get_out;
             }
 
-            fprintf(stderr, " * Added kara %lu to database and set it to unavailable\n",
-                    kara->id);
+            LOG_INFO("Added kara %lu to database and set it to unavailable", kara->id);
 
             break;
 
diff --git a/src/repo/curl.c b/src/repo/curl.c
index 92f71ce4c35f0313a34c624498609947fefec53f..d9acdd14f5d7d04447c9ab833d4fbf75224d37ef 100644
--- a/src/repo/curl.c
+++ b/src/repo/curl.c
@@ -67,7 +67,7 @@ repo_new(struct lkt_repo *const repo_, const char *name_, const char *url_)
     uint64_t *calloc2 = calloc(init_size, sizeof(uint64_t));
     if (!calloc2) {
         free(calloc1);
-        fprintf(stderr, " ! repo_new: Out of memory\n");
+        LOG_ERROR_SCT("MEMORY", "%s", "Out of memory");
         return ENOMEM;
     }
 
@@ -151,8 +151,7 @@ repo_get_alljson_sync(struct lkt_repo *const repo, struct json_object **json)
     res = curl_easy_perform(curl_handle);
 
     if (res != CURLE_OK) {
-        fprintf(stderr, " ! repo_get_alljson_sync: curl_easy_perform failed: %s\n",
-                curl_easy_strerror(res));
+        LOG_ERROR_SCT("CURL", "curl_easy_perform failed: %s", curl_easy_strerror(res));
         free(file.mem);
         goto err;
     }
@@ -192,7 +191,7 @@ repo_get_id(struct lkt_repo *const repo, const uint64_t id, struct kara_metadata
     res = curl_easy_perform(curl_handle);
 
     if (res != CURLE_OK) {
-        fprintf(stderr, " ! repo_get_id: curl_easy_perform failed: %s\n", curl_easy_strerror(res));
+        LOG_ERROR_SCT("CURL", "curl_easy_perform failed: %s", curl_easy_strerror(res));
         free(url);
         free(file.mem);
         curl_easy_cleanup(curl_handle);
@@ -206,12 +205,11 @@ repo_get_id(struct lkt_repo *const repo, const uint64_t id, struct kara_metadata
     jobj = json_tokener_parse(file.mem);
 
     if (json_object_object_get_ex(jobj, "message", &field)) {
-        fprintf(stderr, " * repo_get_id: Kara with id %lu not found, message is: %s\n",
-                id, json_object_get_string(field));
+        LOG_ERROR("Kara with id %lu not found, message is: %s", id, json_object_get_string(field));
         goto err;
     }
 
-    fprintf(stderr, " . repo_get_id: Got kara with id %lu\n", id);
+    LOG_INFO("Got kara with id %lu", id);
 
     err |= safe_json_get_string(jobj, "song_name", mdt->song_name, LEKTOR_TAG_MAX);
     err |= safe_json_get_string(jobj, "source_name", mdt->source_name, LEKTOR_TAG_MAX);
@@ -223,19 +221,19 @@ repo_get_id(struct lkt_repo *const repo, const uint64_t id, struct kara_metadata
     errno = 0;
 
     if (!json_object_object_get_ex(jobj, "song_number", &field)) {
-        fprintf(stderr, " * repo_get_id: No object with key 'song_number' in json, error\n");
+        LOG_ERROR("%s", "No object with key 'song_number' in json, error");
         goto err;
     }
 
     mdt->song_number = json_object_get_int(field);
 
     if (errno == EINVAL) {
-        fprintf(stderr, " * repo_get_id: Invalid integer for field with key 'song_number'\n");
+        LOG_ERROR("%s", "Invalid integer for field with key 'song_number'");
         goto err;
     }
 
     if (mdt->song_number == INT32_MAX || mdt->song_number == INT32_MIN) {
-        fprintf(stderr, " * repo_get_id: Out of bound integer for field with key 'song_number'\n");
+        LOG_ERROR("Out of bound int32_t '%d' for field with key 'song_number'", mdt->song_number);
         goto err;
     }
 
@@ -260,7 +258,7 @@ repo_download_id_sync(struct lkt_repo *const repo, sqlite3 *db, const uint64_t i
     RETURN_UNLESS(url, "Out of memory", errno = ENOMEM);
 
     if (repo_get_id(repo, id, mdt_ret ? mdt_ret : &mdt)) {
-        fprintf(stderr, " ! repo_download_id_sync: Failed to get kara metadata from kurisu\n");
+        LOG_ERROR("%s", "Failed to get kara metadata from kurisu");
         goto err_no_curl;
     }
 
@@ -271,10 +269,10 @@ repo_download_id_sync(struct lkt_repo *const repo, sqlite3 *db, const uint64_t i
 
     if (fd < 0) {
         if (errno == EEXIST)
-            fprintf(stderr, " ! repo_download_id_sync: file '%s' already exists\n", kara_path);
+            LOG_ERROR("File '%s' already exists", kara_path);
 
         else
-            fprintf(stderr, " ! repo_download_id_sync: could not open file '%s'\n", kara_path);
+            LOG_ERROR("Could not open file '%s'", kara_path);
 
         goto err_no_curl;
     }
@@ -297,26 +295,24 @@ repo_download_id_sync(struct lkt_repo *const repo, sqlite3 *db, const uint64_t i
     ret = curl_easy_perform(curl_handle);
 
     if (ret != CURLE_OK) {
-        fprintf(stderr, " ! repo_download_id_sync: curl_easy_perform failed: %s\n",
-                curl_easy_strerror(ret));
+        LOG_ERROR_SCT("CURL", "curl_easy_perform failed: %s", curl_easy_strerror(ret));
         goto err;
     }
 
     if (CURLE_OK == ( ret = curl_easy_getinfo(curl_handle, CURLINFO_CONTENT_TYPE, &ct)))
-        fprintf(stderr, " . repo_download_id_sync: Content-Type is '%s'\n", ct);
+        LOG_INFO_SCT("CURL", "Content-Type is '%s'", ct);
     else {
-        fprintf(stderr, " ! repo_download_id_sync: failed to get Content-Type: %s\n",
-                curl_easy_strerror(ret));
+        LOG_ERROR_SCT("CURL", "Failed to get Content-Type: %s", curl_easy_strerror(ret));
         goto err;
     }
 
     if (!db) {
-        fprintf(stderr, " . repo_download_id_sync: Skip database update here\n");
+        LOG_INFO("%s", "Skip database update here");
         goto no_db_update;
     }
 
     if (! database_update_add(db, kara_path, mdt_ret ? mdt_ret : &mdt, id, true)) {
-        fprintf(stderr, " ! repo_download_id_sync: Failed to add kara to database\n");
+        LOG_ERROR("%s", "Failed to add kara to database");
         goto err;
     }
 
diff --git a/src/repo/downloader.c b/src/repo/downloader.c
index f717a030df74a6ac8ff8a38a4449632b77f516d5..a8036da9bfe5365b02ebcef46504c48112a5cfec 100644
--- a/src/repo/downloader.c
+++ b/src/repo/downloader.c
@@ -4,21 +4,22 @@
 #include <stdio.h>
 #include <unistd.h>
 #include <string.h>
+#include <limits.h>
 
+#include <mthread/mthread.h>
 #include <lektor/macro.h>
 #include <lektor/repo.h>
 #include <lektor/thread.h>
-#include <limits.h>
 
 int
 repo_join_thread(struct lkt_repo *const repo)
 {
     int ret = 1;
     RETURN_IF(pthread_mutex_lock(&repo->mtx), "Failed to lock mutex", 3);
-    GOTO_UNLESS(repo->init, "Repo thread not launched, can't join\n", error);
+    GOTO_UNLESS(repo->init, "Repo thread not launched, can't join", error);
     repo->stop = 1;
     GOTO_IF(pthread_join(repo->self.th, NULL), "Failed to join repo thread", error);
-    fprintf(stderr, " . repo_join_thread: repo thread joined\n");
+    fprintf(stderr, " . repo_join_thread: repo thread joined");
     ret = 0;
 error:
     RETURN_IF(pthread_mutex_unlock(&repo->mtx), "Failed to unlock mutex", 3);
@@ -32,7 +33,7 @@ extern int
 safe_json_get_int32(struct json_object *json, const char *key, int32_t *ret);
 
 static inline void
-__handle_got_json(struct lkt_thread *self, struct lkt_repo *repo, struct json_object *json)
+__handle_got_json(struct poller_thread *self, struct lkt_repo *repo, struct json_object *json)
 {
     size_t i, len = json_object_array_length(json);
     struct json_object *kara_json;
@@ -60,7 +61,7 @@ __handle_got_json(struct lkt_thread *self, struct lkt_repo *repo, struct json_ob
         }
         integer = snprintf(kara->filename + kara_dir_len, PATH_MAX - kara_dir_len, "%d", integer);
         kara->filename[PATH_MAX - 1] = 0;
-        fprintf(stderr, " . __handle_got_json: Crafted filename is '%s'\n", kara->filename);
+        LOG_INFO("Crafted filename is '%s'", kara->filename);
 
         RETURN_UNLESS(kara, "Out of memory", NOTHING);
 
@@ -81,7 +82,7 @@ __handle_got_json(struct lkt_thread *self, struct lkt_repo *repo, struct json_ob
 
         /* Append. */
         if (lkt_th_append_output(self, kara)) {
-            fprintf(stderr, " . __handle_got_json: Could not append downloaded kara mdt\n");
+            LOG_ERROR("%s", "Could not append downloaded kara mdt");
             goto err;
         }
 
@@ -92,23 +93,20 @@ err:
 }
 
 static void *
-__repo_thread_function(struct lkt_thread_arg *arg)
+__repo_thread_function(struct poller_thread_arg *arg)
 {
     size_t head;
     struct lkt_repo *repo = arg->args;
-    struct lkt_thread *self = arg->self;
+    struct poller_thread *self = arg->self;
     struct kara *kara;
     struct json_object *json = NULL;
     char path[PATH_MAX];
     free(arg);
 
-    fprintf(stderr, " . __repo_thread_function: Starting the repo thread\n");
+    LOG_INFO("%s", "Starting the repo thread");
 
     for (;;) {
-        if (pthread_mutex_lock(&repo->mtx)) {
-            fprintf(stderr, " ! __repo_thread_function: Failed to lock mutex\n");
-            goto end_loop;
-        }
+        GOTO_IF(pthread_mutex_lock(&repo->mtx), "Failed to lock mutex", end_loop);
 
         if (repo->all_json) {
             repo_get_alljson_sync(repo, &json);
@@ -118,7 +116,7 @@ __repo_thread_function(struct lkt_thread_arg *arg)
 
         if (repo->stop) {
             if (pthread_mutex_unlock(&repo->mtx))
-                fprintf(stderr, " ! __repo_thread_function: Failed to unlock mutex\n");
+                LOG_ERROR("Failed to unlock mutex: %s", strerror(errno));
             break;
         }
 
@@ -126,7 +124,7 @@ __repo_thread_function(struct lkt_thread_arg *arg)
 
         /* size_t has the size of a pointer (thus of a void *). */
         if (lkt_th_pop_input(self, (void **) &head)) {
-            fprintf(stderr, " * __repo_thread_function: Failed to get the head of the input list\n");
+            LOG_ERROR("%s", "Failed to get the head of the input list");
             goto end_loop; /* Just skip all the loop to the yield function. */
         }
 
@@ -139,12 +137,12 @@ __repo_thread_function(struct lkt_thread_arg *arg)
         kara = calloc(1, sizeof(struct kara));
 
         if (NULL == kara) {
-            fprintf(stderr, " ! __repo_thread_function: Out of memory\n");
+            LOG_ERROR_SCT("MEMORY", "%s", "Out of memory");
             goto end_loop;
         }
 
         if (repo_download_id_sync(repo, NULL, head, path, &kara->mdt)) {
-            fprintf(stderr, " ! __repo_thread_function: Failed to download kara with id %lu\n", head);
+            LOG_ERROR("Failed to download kara with id %lu", head);
             goto try_later;
         }
 
@@ -154,13 +152,12 @@ __repo_thread_function(struct lkt_thread_arg *arg)
         memcpy(kara->filename, path, (strlen(path) + 1) * sizeof(char));
 
         if (lkt_th_append_output(self, (void *) kara)) {
-            fprintf(stderr, " ! __repo_thread_function: Failed to append to output, orphan kara %lu\n", head);
+            LOG_ERROR("Failed to append to output, orphan kara %lu", head);
             free(kara);
             goto end_loop;
         }
 
-        fprintf(stderr, " . __repo_thread_function: Append kara %lu with path %s to out pool\n",
-                kara->id, kara->filename);
+        LOG_INFO("Append kara %lu with path %s to out pool", kara->id, kara->filename);
 
         kara = NULL;
         goto end_loop;
@@ -171,13 +168,14 @@ try_later:
 
         /* Retry later. TODO: Implements a retry counter. */
         if (lkt_th_append_input(self, (void *) head))
-            fprintf(stderr, " * __repo_thread_function: Failed to get the head of the input list\n");
+            LOG_ERROR("%s", "Failed to get the head of the input list");
 
 end_loop:
+        sched_yield();
         sleep(1);
     }
 
-    fprintf(stderr, " . __repo_thread_function: Stopping the repo thread\n");
+    LOG_INFO("%s", "Stopping the repo thread");
     return NULL;
 }
 
@@ -186,7 +184,7 @@ repo_new_thread(struct lkt_repo *const repo)
 {
     RETURN_UNLESS(repo, "Invalid argument", 1);
     RETURN_IF(repo->init, "Already running", 1);
-    struct lkt_thread_arg *arg = calloc(1, sizeof(struct lkt_thread_arg));
+    struct poller_thread_arg *arg = calloc(1, sizeof(struct poller_thread_arg));
     RETURN_UNLESS(arg, "Out of memory", errno = ENOMEM);
     arg->args = repo;
     RETURN_IF(lkt_th_new(&repo->self, LKT_DEFAULT_LIST_SIZE, __repo_thread_function, arg), "Thread error", 1);
@@ -199,7 +197,7 @@ repo_download_id_async(struct lkt_repo *const repo, const size_t id)
 {
     RETURN_IF(id == 0, "Invalid argument", 1);
     RETURN_IF(lkt_th_append_input(&repo->self, (void *) id), "Failed to push downloaded id", id);
-    fprintf(stderr, " * Asked to download kara with id %lu\n", id);
+    LOG_INFO("Asked to download kara with id %lu", id);
     return 0;
 }
 
diff --git a/src/thread.c b/src/thread.c
index 7d7b1a4666375d762fc01a79dc87f5c49138b905..4969c93320d752211f0c7482bbf487dced727a33 100644
--- a/src/thread.c
+++ b/src/thread.c
@@ -1,5 +1,6 @@
 #define _POSIX_C_SOURCE 200809L
 
+#include <common/common.h>
 #include <lektor/thread.h>
 #include <pthread.h>
 #include <sys/types.h>
@@ -10,28 +11,27 @@
 #include <string.h>
 
 struct __args {
-    void *(*start)(struct lkt_thread_arg *);
-    struct lkt_thread_arg *arg;
+    void *(*start)(struct poller_thread_arg *);
+    struct poller_thread_arg *arg;
 };
 
 static void *
 __start(void *args__)
 {
     struct __args *args = (struct __args *) args__;
-    void *(*start)(struct lkt_thread_arg *) = args->start;
-    struct lkt_thread_arg *arg = args->arg;
+    void *(*start)(struct poller_thread_arg *) = args->start;
+    struct poller_thread_arg *arg = args->arg;
     free(args__);
     return start(arg);
 }
 
 int
-lkt_th_new(struct lkt_thread *th, unsigned int init_sizes,
-           void *(*func)(struct lkt_thread_arg *), void *args)
+lkt_th_new(struct poller_thread *th, unsigned int init_sizes,
+           void *(*func)(struct poller_thread_arg *), void *args)
 {
-    int ret = 1;
     pthread_mutex_t mtx1 = PTHREAD_MUTEX_INITIALIZER;
     pthread_mutex_t mtx2 = PTHREAD_MUTEX_INITIALIZER;
-    struct lkt_thread th_ = {
+    struct poller_thread th_ = {
         .initial_size   = init_sizes,
         .input_lock     = mtx1,
         .output_lock    = mtx2,
@@ -52,34 +52,33 @@ lkt_th_new(struct lkt_thread *th, unsigned int init_sizes,
     __args->arg = args;
     __args->arg->self = th;
 
-    ret = pthread_create(&(th->th), NULL, __start, __args);
-
-    if (ret)
-        goto end;
+    if (!pthread_create(&(th->th), NULL, __start, __args)) {
+        LOG_INFO_SCT("THREAD", "%s", "Create a new poller thread");
+        return 0;
+    }
 
-end:
-    fprintf(stderr, " %c lkt_th_new: %s\n",
-            ret ? '!' : '*',
-            ret ? "Failed to create a new thread" : "Created a new thread");
-    return ret;
+    else {
+error:
+        LOG_ERROR_SCT("THREAD", "%s", "Failed to create a poller thread");
+        return 1;
+    }
 
 out_of_memory:
-    fprintf(stderr, " * lkt_th_new: Out of memory\n");
+    LOG_ERROR_SCT("MEMORY", "%s", "Out of memory");
     if (th_.input_cells)
         free((void *) th_.input_cells);
     if (th_.output_cells)
         free((void *) th_.output_cells);
-    ret = errno = ENOMEM;
-    goto end;
+    goto error;
 }
 
 int
-lkt_th_join(struct lkt_thread *th, void **ret)
+lkt_th_join(struct poller_thread *th, void **ret)
 {
     int sta = pthread_join(th->th, ret);
 
     if (sta)
-        fprintf(stderr, " ! lkt_th_join: Failed to join thread\n");
+        LOG_ERROR("%s", "Failed to join thread");
 
     if (th->input_cells)
         free((void *) th->input_cells);
@@ -87,9 +86,9 @@ lkt_th_join(struct lkt_thread *th, void **ret)
         free((void *) th->output_cells);
 
     if (!sta)
-        fprintf(stderr, " * lkt_th_join: Thread joined\n");
+        LOG_ERROR("%s", "Thread joined");
 
-    memset(th, 0, sizeof(struct lkt_thread));
+    memset(th, 0, sizeof(struct poller_thread));
     return sta;
 }
 
@@ -100,16 +99,13 @@ th_append(unsigned int *len, unsigned int *size, void ***cells,
     void *new;
     int ret = 0;
 
-    if (pthread_mutex_lock(lock)) {
-        fprintf(stderr, " ! th_append: Failed to lock\n");
-        goto end;
-    }
+    GOTO_IF(pthread_mutex_lock(lock), "Failed to lock", end);
 
     if (*len == *size) {
         new = realloc((void *) *cells, (*len + *size) * sizeof(void *));
 
         if (new == NULL) {
-            fprintf(stderr, " ! th_append: Out of memory\n");
+            LOG_ERROR_SCT("MEMORY", "%s", "Out of memory");
             ret = (errno = ENOMEM);
             goto end;
         }
@@ -121,11 +117,7 @@ th_append(unsigned int *len, unsigned int *size, void ***cells,
     (*cells)[(*len)++] = ptr;
     ret = 0;
 end:
-    if (pthread_mutex_unlock(lock)) {
-        fprintf(stderr, " ! th_append: Failed to lock\n");
-        ret = 1;
-    }
-
+    RETURN_IF(pthread_mutex_unlock(lock), "Failed to lock", 1);
     return ret;
 }
 
@@ -134,10 +126,7 @@ th_pop(unsigned int *len, void **cells, pthread_mutex_t *lock, void **ptr)
 {
     int ret = 1;
 
-    if (pthread_mutex_lock(lock)) {
-        fprintf(stderr, " ! th_pop: Failed to lock\n");
-        goto end;
-    }
+    GOTO_IF(pthread_mutex_lock(lock), "Failed to lock", end);
 
     if (*len > 0)
         *ptr = cells[--(*len)];
@@ -146,12 +135,7 @@ th_pop(unsigned int *len, void **cells, pthread_mutex_t *lock, void **ptr)
 
     ret = 0;
 end:
-
-    if (pthread_mutex_unlock(lock)) {
-        fprintf(stderr, " ! th_pop: Failed to lock\n");
-        ret = 1;
-    }
-
+    RETURN_IF(pthread_mutex_unlock(lock), "Failed to lock", 1);
     return ret;
 }
 
@@ -159,11 +143,7 @@ static inline int
 th_head(unsigned int len, void **cells, pthread_mutex_t *lock, void **ptr)
 {
     int ret = 1;
-
-    if (pthread_mutex_lock(lock)) {
-        fprintf(stderr, " ! th_pop: Failed to lock\n");
-        goto end;
-    }
+    GOTO_IF(pthread_mutex_lock(lock), "Failed to lock", end);
 
     if (len > 0)
         *ptr = cells[len - 1];
@@ -172,31 +152,26 @@ th_head(unsigned int len, void **cells, pthread_mutex_t *lock, void **ptr)
 
     ret = 0;
 end:
-
-    if (pthread_mutex_unlock(lock)) {
-        fprintf(stderr, " ! th_pop: Failed to lock\n");
-        ret = 1;
-    }
-
+    RETURN_IF(pthread_mutex_unlock(lock), "Failed to lock", 1);
     return ret;
 }
 
 int
-lkt_th_append_input(struct lkt_thread *th, void *ptr)
+lkt_th_append_input(struct poller_thread *th, void *ptr)
 {
     return th_append((unsigned int *) &th->input_len, (unsigned int *) &th->input_size,
                      (void ***) &th->input_cells, &th->input_lock, ptr);
 }
 
 int
-lkt_th_append_output(struct lkt_thread *th, void *ptr)
+lkt_th_append_output(struct poller_thread *th, void *ptr)
 {
     return th_append((unsigned int *) &th->output_len, (unsigned int *) &th->output_size,
                      (void ***) &th->output_cells, &th->output_lock, ptr);
 }
 
 int
-lkt_th_pop_input(struct lkt_thread *th, void **ptr)
+lkt_th_pop_input(struct poller_thread *th, void **ptr)
 {
     return th_pop((unsigned int *) &th->input_len, (void **) th->input_cells,
                   &th->input_lock, ptr);
@@ -204,20 +179,20 @@ lkt_th_pop_input(struct lkt_thread *th, void **ptr)
 
 
 int
-lkt_th_pop_output(struct lkt_thread *th, void **ptr)
+lkt_th_pop_output(struct poller_thread *th, void **ptr)
 {
     return th_pop((unsigned int *) &th->output_len, (void **) th->output_cells,
                   &th->output_lock, ptr);
 }
 
 int
-lkt_th_head_input(struct lkt_thread *th, void **ptr)
+lkt_th_head_input(struct poller_thread *th, void **ptr)
 {
     return th_head((unsigned int) th->input_len, (void **) th->input_cells, &th->input_lock, ptr);
 }
 
 int
-lkt_th_head_output(struct lkt_thread *th, void **ptr)
+lkt_th_head_output(struct poller_thread *th, void **ptr)
 {
     return th_head((unsigned int) th->output_len, (void **) th->output_cells, &th->output_lock, ptr);
 }