diff --git a/src/application/sdl-app.cpp b/src/application/sdl-app.cpp
index a2c005b7..01736fda 100644
--- a/src/application/sdl-app.cpp
+++ b/src/application/sdl-app.cpp
@@ -177,7 +177,7 @@ SdlApp::SdlApp(char const *title, ivec2 res, float fps) :
     ivec2 screen_size = res;
 
     /* Initialise everything */
-    Ticker::Setup(fps);
+    ticker::setup(fps);
     audio::init();
 
     /* Autoreleased objects */
@@ -210,6 +210,7 @@ SdlApp::~SdlApp()
     SDL_Quit();
 #endif
     delete data;
+    ticker::teardown();
 }
 
 } /* namespace lol */
diff --git a/src/engine/entity.cpp b/src/engine/entity.cpp
index 14c86432..dac9bc3b 100644
--- a/src/engine/entity.cpp
+++ b/src/engine/entity.cpp
@@ -27,7 +27,7 @@ Entity::Entity() :
     m_destroy(0)
 {
 #if !LOL_BUILD_RELEASE
-    m_tickstate = STATE_IDLE;
+    m_tickstate = tickable::state::idle;
 #endif
     m_gamegroup = tickable::group::game::entity;
     m_drawgroup = tickable::group::draw::entity;
@@ -61,9 +61,9 @@ void Entity::tick_game(float seconds)
 {
     UNUSED(seconds);
 #if !LOL_BUILD_RELEASE
-    if (m_tickstate != STATE_PRETICK_GAME)
+    if (m_tickstate != tickable::state::pre_game)
         msg::error("invalid entity game tick\n");
-    m_tickstate = STATE_POSTTICK_GAME;
+    m_tickstate = tickable::state::post_game;
 #endif
 }
 
@@ -71,22 +71,11 @@ void Entity::tick_draw(float seconds, Scene &scene)
 {
     UNUSED(seconds, scene);
 #if !LOL_BUILD_RELEASE
-    if (m_tickstate != STATE_PRETICK_DRAW)
+    if (m_tickstate != tickable::state::pre_draw)
         msg::error("invalid entity draw tick\n");
-    m_tickstate = STATE_POSTTICK_DRAW;
+    m_tickstate = tickable::state::post_draw;
 #endif
 }
 
-void Entity::SetState(uint32_t state)
-{
-    Ticker::SetState(this, state);
-}
-
-void Entity::SetStateWhenMatch(uint32_t state,
-                               Entity *other_entity, uint32_t other_state)
-{
-    Ticker::SetStateWhenMatch(this, state, other_entity, other_state);
-}
-
 } /* namespace lol */
 
diff --git a/src/engine/entity.h b/src/engine/entity.h
index 816ad519..de6c2297 100644
--- a/src/engine/entity.h
+++ b/src/engine/entity.h
@@ -47,8 +47,7 @@ class Entity
 {
     friend class Scene;
     friend class ticker;
-    friend class TickerData;
-    friend class Emcee;
+    friend class ticker_data;
 
 public:
     virtual std::string GetName() const;
@@ -70,33 +69,12 @@ protected:
     InitState m_initstate;
 
 #if !LOL_BUILD_RELEASE
-    enum
-    {
-        STATE_IDLE = 0,
-        STATE_PRETICK_GAME,
-        STATE_POSTTICK_GAME,
-        STATE_PRETICK_DRAW,
-        STATE_POSTTICK_DRAW,
-    }
-    m_tickstate;
+    tickable::state m_tickstate;
 #endif
 
     tickable::group::game m_gamegroup;
     tickable::group::draw m_drawgroup;
 
-    // Emcee begin
-private:
-    void SetState(uint32_t newstate);
-    void SetStateWhenMatch(uint32_t newstate,
-                           Entity *other_entity, uint32_t other_state);
-    virtual uint32_t OnStateChanged(uint32_t newstate)
-    {
-        return LOLm_state = newstate;
-    }
-
-    uint32_t LOLm_state;
-    // Emcee end
-
 private:
     int m_ref, m_autorelease, m_destroy;
     uint64_t m_scene_mask = 0;
diff --git a/src/engine/ticker.cpp b/src/engine/ticker.cpp
index cea13b5a..1951388a 100644
--- a/src/engine/ticker.cpp
+++ b/src/engine/ticker.cpp
@@ -23,13 +23,13 @@ namespace lol
  * Ticker implementation class
  */
 
-static class TickerData
+class ticker_data
 {
     friend class ticker;
 
 public:
-    TickerData() :
-        DEPRECATED_nentities(0),
+    ticker_data()
+      : DEPRECATED_nentities(0),
         frame(0), recording(0), deltatime(0), bias(0), fps(0),
 #if LOL_BUILD_DEBUG
         keepalive(0),
@@ -38,7 +38,7 @@ public:
     {
     }
 
-    ~TickerData()
+    ~ticker_data()
     {
         ASSERT(DEPRECATED_nentities == 0,
                "still %d entities in ticker\n", DEPRECATED_nentities);
@@ -51,6 +51,7 @@ public:
         disktick.push(0);
         gamethread.release();
         diskthread.release();
+        ASSERT(drawtick.size() == 0);
 #endif
     }
 
@@ -90,10 +91,9 @@ private:
 
     /* Shutdown management */
     int quit, quitframe, quitdelay, panic;
-}
-tickerdata;
+};
 
-static TickerData * const data = &tickerdata;
+static std::unique_ptr<ticker_data> data;
 
 //
 // Add/remove tickable objects
@@ -167,7 +167,7 @@ int Ticker::Unref(Entity *entity)
 }
 
 #if LOL_FEATURE_THREADS
-void TickerData::GameThreadMain()
+void ticker_data::GameThreadMain()
 {
 #if LOL_BUILD_DEBUG
     msg::debug("ticker game thread initialised\n");
@@ -193,7 +193,7 @@ void TickerData::GameThreadMain()
 #endif /* LOL_FEATURE_THREADS */
 
 #if LOL_FEATURE_THREADS
-void TickerData::DrawThreadMain() /* unused */
+void ticker_data::DrawThreadMain() /* unused */
 {
 #if LOL_BUILD_DEBUG
     msg::debug("ticker draw thread initialised\n");
@@ -217,7 +217,7 @@ void TickerData::DrawThreadMain() /* unused */
 #endif /* LOL_FEATURE_THREADS */
 
 #if LOL_FEATURE_THREADS
-void TickerData::DiskThreadMain()
+void ticker_data::DiskThreadMain()
 {
     /* FIXME: temporary hack to avoid crashes on the PS3 */
     disktick.pop();
@@ -225,7 +225,7 @@ void TickerData::DiskThreadMain()
 #endif /* LOL_FEATURE_THREADS */
 
 //-----------------------------------------------------------------------------
-void TickerData::GameThreadTick()
+void ticker_data::GameThreadTick()
 {
     Profiler::Stop(Profiler::STAT_TICK_FRAME);
     Profiler::Start(Profiler::STAT_TICK_FRAME);
@@ -419,17 +419,17 @@ void TickerData::GameThreadTick()
             if (!e->m_destroy)
             {
 #if !LOL_BUILD_RELEASE
-                if (e->m_tickstate != Entity::STATE_IDLE)
+                if (e->m_tickstate != tickable::state::idle)
                     msg::error("entity %s [%p] not idle for game tick\n",
                                e->GetName().c_str(), e);
-                e->m_tickstate = Entity::STATE_PRETICK_GAME;
+                e->m_tickstate = tickable::state::pre_game;
 #endif
                 e->tick_game(data->deltatime);
 #if !LOL_BUILD_RELEASE
-                if (e->m_tickstate != Entity::STATE_POSTTICK_GAME)
+                if (e->m_tickstate != tickable::state::post_game)
                     msg::error("entity %s [%p] missed super game tick\n",
                                e->GetName().c_str(), e);
-                e->m_tickstate = Entity::STATE_IDLE;
+                e->m_tickstate = tickable::state::idle;
 #endif
             }
         }
@@ -439,7 +439,7 @@ void TickerData::GameThreadTick()
 }
 
 //-----------------------------------------------------------------------------
-void TickerData::DrawThreadTick()
+void ticker_data::DrawThreadTick()
 {
     Profiler::Start(Profiler::STAT_TICK_DRAW);
 
@@ -473,17 +473,17 @@ void TickerData::DrawThreadTick()
                 if (!e->m_destroy)
                 {
 #if !LOL_BUILD_RELEASE
-                    if (e->m_tickstate != Entity::STATE_IDLE)
+                    if (e->m_tickstate != tickable::state::idle)
                         msg::error("entity %s [%p] not idle for draw tick\n",
                                    e->GetName().c_str(), e);
-                    e->m_tickstate = Entity::STATE_PRETICK_DRAW;
+                    e->m_tickstate = tickable::state::pre_draw;
 #endif
                     e->tick_draw(data->deltatime, scene);
 #if !LOL_BUILD_RELEASE
-                    if (e->m_tickstate != Entity::STATE_POSTTICK_DRAW)
+                    if (e->m_tickstate != tickable::state::post_draw)
                         msg::error("entity %s [%p] missed super draw tick\n",
                                    e->GetName().c_str(), e);
-                    e->m_tickstate = Entity::STATE_IDLE;
+                    e->m_tickstate = tickable::state::idle;
 #endif
                 }
             }
@@ -501,7 +501,7 @@ void TickerData::DrawThreadTick()
     Profiler::Stop(Profiler::STAT_TICK_DRAW);
 }
 
-void TickerData::DiskThreadTick()
+void ticker_data::DiskThreadTick()
 {
     ;
 }
@@ -517,28 +517,35 @@ void Ticker::SetStateWhenMatch(Entity * /* entity */, uint32_t /* state */,
 
 }
 
-//-----------------------------------------------------------------------------
-void Ticker::Setup(float fps)
+void ticker::setup(float fps)
 {
+    data = std::make_unique<ticker_data>();
     data->fps = fps;
 
 #if LOL_FEATURE_THREADS
-    data->gamethread = std::make_unique<thread>(std::bind(&TickerData::GameThreadMain, data));
+    data->gamethread = std::make_unique<thread>(std::bind(&ticker_data::GameThreadMain, data.get()));
     data->drawtick.push(1);
 
-    data->diskthread = std::make_unique<thread>(std::bind(&TickerData::DiskThreadMain, data));
+    data->diskthread = std::make_unique<thread>(std::bind(&ticker_data::DiskThreadMain, data.get()));
 #endif
 }
 
-void Ticker::tick_draw()
+void ticker::teardown()
+{
+    data.release();
+}
+
+void ticker::tick_draw()
 {
 #if LOL_FEATURE_THREADS
-    data->drawtick.pop();
+    int n = data->drawtick.pop();
+    if (n == 0)
+        return;
 #else
-    TickerData::GameThreadTick();
+    ticker_data::GameThreadTick();
 #endif
 
-    TickerData::DrawThreadTick();
+    ticker_data::DrawThreadTick();
 
     Profiler::Start(Profiler::STAT_TICK_BLIT);
 
@@ -546,7 +553,7 @@ void Ticker::tick_draw()
 #if LOL_FEATURE_THREADS
     data->gametick.push(1);
 #else
-    TickerData::DiskThreadTick();
+    ticker_data::DiskThreadTick();
 #endif
 
     /* Clamp FPS */
diff --git a/src/engine/ticker.h b/src/engine/ticker.h
index 92438d17..84eca0a7 100644
--- a/src/engine/ticker.h
+++ b/src/engine/ticker.h
@@ -29,6 +29,10 @@ namespace lol
 class ticker
 {
 public:
+    static void setup(float fps);
+    static void tick_draw();
+    static void teardown();
+
     static void add(std::shared_ptr<tickable> entity);
     static void remove(std::shared_ptr<tickable> entity);
 
@@ -37,8 +41,6 @@ public:
     static void Ref(class Entity *entity);
     static int Unref(class Entity *entity);
 
-    static void Setup(float fps);
-    static void tick_draw();
     static void StartBenchmark();
     static void StopBenchmark();
     static void StartRecording();
diff --git a/src/lol/engine/tickable.h b/src/lol/engine/tickable.h
index 8888134c..ec832764 100644
--- a/src/lol/engine/tickable.h
+++ b/src/lol/engine/tickable.h
@@ -45,6 +45,15 @@ public:
         return p;
     }
 
+    enum class state
+    {
+        idle,
+        pre_game,
+        post_game,
+        pre_draw,
+        post_draw,
+    };
+
     // Tick groups
     struct group
     {
diff --git a/src/lol/sys/thread.h b/src/lol/sys/thread.h
index 2a9e1ac2..501edefc 100644
--- a/src/lol/sys/thread.h
+++ b/src/lol/sys/thread.h
@@ -1,7 +1,7 @@
 //
 //  Lol Engine
 //
-//  Copyright © 2010—2017 Sam Hocevar <sam@hocevar.net>
+//  Copyright © 2010—2019 Sam Hocevar <sam@hocevar.net>
 //
 //  Lol Engine is free software. It comes without any warranty, to
 //  the extent permitted by applicable law. You can redistribute it
@@ -94,8 +94,9 @@ public:
     queue()
       : m_start(0),
         m_count(0)
-    {
-    }
+    {}
+
+    int size() const { return m_count; }
 
     // Will block the thread if another has already locked
     void push(T value)